I have a button class. I want the button class's constructor to take the function it will call when the button is pressed. This would be easy if the button class was only taking a function from one class, but the purpose of the button's constructor taking a function is that no matter what class the button is created in it will be able to store a pointer to a function in the class it's created in.
To illustrate:
struct Button {
Button(void (*functionPtr)()) {
// Store the function pointer to be called later
}
};
struct SomeClass {
void LoadFile();
SomeClass() {
Button* temp1 = new Button(&LoadFile); // ???
}
};
struct AnotherClass {
void SaveFile();
SomeClass() {
Button* temp2 = new Button(&SaveFile); // ???
}
};
How can I make that work?
You can't really have a pointer to a member function because it's meaningless w/o the hidden
this
pointer on which the object is invoked.There are a few common solutions to the problem you're trying to solve.
The
C
way:Store a
void*
in addition to the function pointer. Then pass thevoid*
int the callback. This is less "safe", but is common in C.OOP way #1:
Subclass
Button
and have a virtual function on the base class, sayonPress
that can be implemented in the subclasses.OOP way #2:
Have a callback interface independent of
Button
that your custom classes implement.EDIT: Or use lambdas as mentioned in comments. I am still re-learning C++ the modern way myself.
A pointer-to-function and a pointer-to-member-function, despite seeming pretty similar, are actually entirely different beasts.
void (*functionPtr)()
is a pointer to a function that takes no arguments and returns void.&AnotherClass::SaveFile
is a pointer to a member function ofAnotherClass
... its type isvoid (AnotherClass::*)()
. Notice that the class name is part of the type, so you can't simply store a pointer-to-member-function to an arbitrary class. Furthermore, to call a pointer-to-member-function, you need an instance pointer - you'd have to store that somehow, but those would have different types too!What you could do instead in C++11 is use type-erasure:
And assign an arbitrary callable to it:
And then you could create a button using
std::bind
:Now
temp1->callback()
will actually callLoadFile()
on the instance ofOtherClass
that it was constructed with. Which is what you wanted. And, we can still use free functions:Insert the usual caveats about using raw pointers and
new
and preferringunique_ptr
.