简体   繁体   English

如何创建指向函数指针的指针?

[英]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.我正在尝试创建一个具有 2 个函数的结构,如果以后需要可以重写它。 The functions are: onClicked() and onClickedRight().函数是:onClicked() 和 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.onClicked是一个函数时, &onClickedonClicked计算结果相同——指向函数的指针。

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.调用看起来像(*(aButton.onRightClick))() ,您需要让onRightClick指向指向函数的指针对象,而不是直接分配函数。

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 .我想您正在寻找一种方法来定义onRightClick应默认“继承” onClick的行为,除非用户覆盖此行为并将不同的行为分配给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 1) 如果onRightClick没有被覆盖,它将继承对onClick所做的每个更改

2) If onRightClick gets overridden, it gets decoupled from onClick . 2) 如果onRightClick被覆盖,它将与onClick分离。

You can solve this with "simple" function pointers by assigning onRightClick a function that simply calls the one assigned to onClick .您可以使用“简单”函数指针来解决这个问题,方法是为onRightClick分配一个函数,该函数只调用分配给onClick的函数。 The following code shows this for C++;以下代码为 C++ 显示了这一点; the approach can be transferred to C (though you then need to pass the "this" to the function calling onClick :该方法可以转移到C(尽管您需要将“this”传递给调用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.答案已调整,因为 OP 已从标签列表中删除了

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.如果该结构被复制到另一个结构,则副本中的onClickedRight成员不会指向其自身实例中的onClicked指针。 It is instead pointing to the onClicked pointer of the original instance.相反,它指向原始实例的onClicked指针。

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).您可能需要某种深度复制方法(因此,根据TRoT ,您需要一个复制构造函数、一个赋值运算符和一个析构函数)。

In any case, the C++ code is not really idiomatic.在任何情况下,C++ 代码都不是真正惯用的。 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 .您已经有了一些调用者必须执行的逻辑( if (onClicked) ),因此这可以最大限度地减少调用者犯错的可能性。

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.您可以将其与std::function版本结合使用,测试是否为空,而不是要求由执行默认操作的 lambda 表示“空”。 And if there are multiple handlers you want to have default fallback you could reduce boilerplate by making a template Fire function.如果有多个处理程序您想要默认回退,您可以通过制作模板 Fire 函数来减少样板。


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 .另一种可能有效的方法是创建一个与std::function语义相似的自定义处理程序类型,但如果没有设置std::function ,它的()运算符将执行默认操作。

In the C language functions pointers are the only place where hiding pointers behind typedefs makes sense在 C 语言函数中,指针是唯一可以将指针隐藏在 typedef 后面的地方

https://godbolt.org/z/Gb_WEy 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");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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