简体   繁体   中英

How to create a pointer to a function pointer?

I'm trying to create a struct that have 2 function, which may be rewritten if needed later. The functions are: onClicked() and onClickedRight(). The code for the struct:

typedef struct {
    QString text;
    QString infoText;
    QUrl iconSrc;
    QColor iconColor;
    void (*onClicked)() = nullptr;
    void (*(*onClickedRight))() = &onClicked; // by default, execute the same function from onClicked()
} ConfigButton;

How I'm trying to execute these functions:

ConfigButton b;
...
// test if click funtion has been defined, to execute it
if (b.onClicked)
    b.onClicked(); // this one work just fine

...

if (*(b.onClickedRight))
    (*(b.onClickedRight))(); // this one crashed

Is it even possible? Am I missing something?

When onClicked is a function, both &onClicked and onClicked evaluate to the same thing -- a pointer to the function.

If you want to create a pointer to a function pointer, you need a pointer to a function as a variable first.

However, given your usage, you need just a pointer to a function.

typedef struct {
    QString text;
    QString infoText;
    QUrl iconSrc;
    QColor iconColor;
    void (*onClicked)() = nullptr;
    void (*onClickedRight)() = onClicked;
} ConfigButton;

and

if ( b.onClickedRight)
    b.onClickedRight();

I think you can still solve your issue with a pointer to a function pointer, yet it is a bit clumsy, since you have to call this pointer-pointer in a different manner than you do with a "normal" function pointer. The call would look like (*(aButton.onRightClick))() , and you need to let onRightClick point to a pointer object pointing to a function rather than assigning the function directly.

I suppose you are looking for a way to define that onRightClick shall by default "inherit" the behaviour of onClick , unless the user overrides this and assigns a different behaviour to onRightClick . I see two requirements that a possible solution shall fulfill:

1) If onRightClick did not get overridden, it shall inherit every change made to onClick

2) If onRightClick gets overridden, it gets decoupled from onClick .

You can solve this with "simple" function pointers by assigning onRightClick a function that simply calls the one assigned to onClick . The following code shows this for C++; the approach can be transferred to C (though you then need to pass the "this" to the function calling onClick :

void defaultOnClick() {
    std::cout << "defaultOnClick" << std::endl;
}

void otherClick() {
    std::cout << "otherClick" << std::endl;
}

void rightClick() {
    std::cout << "rightClick" << std::endl;
}


typedef std::function<void(void)> onClickFunctionType;

struct ConfigButton {
    onClickFunctionType onClick = defaultOnClick;
    onClickFunctionType onRightClick = [this](){ this->onClick(); };
} ;

int main() {

    ConfigButton configButton;

    std::cout << "default for both onClick and onRightClick (calling onClick)" << std::endl;
    configButton.onClick();
    configButton.onRightClick();

    std::cout << "special click for onClick; 'inherited' by onRightClick" << std::endl;
    configButton.onClick = otherClick;
    configButton.onClick();
    configButton.onRightClick();

    std::cout << "special click for onClick; different one for onRightClick" << std::endl;
    configButton.onRightClick = rightClick;
    configButton.onClick();
    configButton.onRightClick();

}

Output:

default for both onClick and onRightClick (calling onClick)
defaultOnClick
defaultOnClick
special click for onClick; 'inherited' by onRightClick
otherClick
otherClick
special click for onClick; different one for onRightClick
otherClick
rightClick

Answer adjusted, since OP has removed from the tag list.

The code works as is . So you are doing something else wrong.

However, using a pointer to a pointer to a function in this way may not have the semantics that you want. If the structure gets copied to another structure, the onClickedRight member in the copy is not pointing to the onClicked pointer in its own instance. It is instead pointing to the onClicked pointer of the original instance.

a.onClickedRight = &a.onClicked;
b = a;
assert(b.onClickedRight == &a.onClicked); // Is this intentional?

What this means is that you have to be extra careful about how you use structures that contain pointers to its own members (and pointers to anything, really). You will probably need some kind of deep copy method (so, according to TRoT , you need a copy constructor, an assignment operator, and a destructor).

In any case, the C++ code is not really idiomatic. For myself, I would probably leverage virtual methods. The virtual method syntax can easily accommodate this use case.

struct ConfigButton {
    QString text;
    QString infoText;
    QUrl iconSrc;
    QColor iconColor;
    virtual void onClicked() const = 0;
    virtual void onClickedRight () const { onClicked(); }
};

struct Foo : ConfigButton {
    void onClicked () const {
        //...
    }
};

If you follow this method, this will also work.

One possible approach is to have functions that implement the logic of firing the handler. You already have some logic ( if (onClicked) ) that the caller has to do anyway, so this minimizes the possibility of the caller making a mistake .

struct ConfigButton {
    // ...

    void Fire_OnClicked()
    {
         if ( onClicked )
             onClicked();
    }
    void Fire_OnClickedRight()
    {
         if ( onClickedRight )
             onClickedRight();
         else
             Fire_OnClicked();
    }

private: 
    void (*onClicked)() = nullptr;
    void (*onClickedRight)() = nullptr;
};

You could combine this with the std::function version, testing for empty instead of requiring "empty" to be represented by a lambda performing the default action. And if there are multiple handlers you want to have default fallback you could reduce boilerplate by making a template Fire function.


Another approach that might work would be to make a custom handler type with similar semantics to std::function but its () operator will perform a default action if no function has been set .

In the C language functions pointers are the only place where hiding pointers behind typedefs makes sense

https://godbolt.org/z/Gb_WEy

#include <stdio.h>

typedef int (*fptr_t)();

typedef struct
{
    fptr_t fptr;
    fptr_t *pfptr;
    fptr_t **ppfptr;
    fptr_t ***pppfptr;
}MYSTRUCT_t;

int foo(char *caller)
{
    printf("Function name = %s, caller = %s\n", __FUNCTION__, caller);
    return 0;
}

int main()
{
    MYSTRUCT_t mystr;

    mystr.fptr = foo;
    mystr.pfptr = &mystr.fptr;
    mystr.ppfptr = &mystr.pfptr;
    mystr.pppfptr = &mystr.ppfptr;

    printf("mystr.fptr=%p mystr.pfptr=%p func=%p\n", (void *)mystr.fptr, (void *)mystr.pfptr, (void *)&foo);

    foo("foo");
    mystr.fptr("mystr.fptr");
    (*mystr.pfptr)("mystr.pfptr");
    (*(*mystr.ppfptr))("mystr.ppfptr");
    (*(*(*mystr.pppfptr)))("mystr.pppfptr");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM