Reputation: 61
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?
Upvotes: 5
Views: 310
Reputation: 141544
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 .
Upvotes: 1
Reputation: 70382
Answer adjusted, since OP has removed c 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.
Upvotes: 1
Reputation: 67476
In the C language functions pointers are the only place where hiding pointers behind typedefs makes sense
#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");
}
Upvotes: 0
Reputation: 35154
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
Upvotes: 2
Reputation: 206567
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();
Upvotes: 2