Reputation: 5273
In class A
I have a member function that should receive a pointer to functions from other classes. All those functions have the same signature (receive type and return type).
Something like this:
class A{
private:
void (*onConfirmCallback) (); //pointer to function
public:
void setOnConfirmListener(void (*callback) ()); //member function that receives another member function from another class, and uses the pointer above to point to it
}
definitions:
void A::setOnConfirmListener(void (*callback)())
{
onConfirmCallback = callback;
}
and at some point in class A
I call the callback:
onConfirmCallback();
In class B
I set the callback:
class B{
private:
A a;
public:
B();
foo();
}
and definitions:
B::B(){
a.setOnConfirmListener(foo);
}
B::foo(){
cout << "foo called" << endl;
}
I also have another class C
which also has an instance of A
, and also sets a callback:
class C{
private:
A a;
public:
C();
foo2();
}
and definitions:
C::C(){
a.setOnConfirmListener(foo2);
}
C::foo2(){
cout << "foo2 called" << endl;
}
I tried different declaration variations, and the above code generates this error:
no matching function for call to 'A::setOnConfirmListener()'
I understand that "pointers to functions" differ from "pointers to member functions". So I also tried changing void (*onConfirmCallback) ()
to void (B::*onConfirmCallback) ()
, but I don't think that's good because this pointer should hold callbacks to different classes (that don't derive from the same base class) and not only B
.
Is there a way to implement this?
Basically I'm trying to do something like Java's interface...
Upvotes: 1
Views: 402
Reputation: 119847
Use std::function
rather than function pointers.
class A {
private:
std::function <void()> onConfirmCallback; //pointer to function
public:
void setOnConfirmListener(std::function <void()>);
};
You can pass non-member functions directly to setOnConfirmListener
. When it comes to member functions, you need an object in order to call them:
class B {
private:
A a;
public:
B();
void foo();
};
B::foo(); // invalid and makes no sense
B* b = new B; b->foo(); // OK
so the line below won't work either:
a.setOnConfirmListener(&B::foo); // invalid and makes no sense
You can pass an object together with its member function using std::bind
:
a.setOnConfirmListener(std::bind(&B::foo, b)); // OK
b
can be either B&
, B*
or std::shared_ptr<B>
.
It could be a bit dangerous to use this
(or *this
) as the second argument for bind
because you are now responsible to monitor when your object ceases to exist and unregister all its associated listeners. One way round this is to derive your objects from enable_shared_from_this
and use std::shared_ptr
instead of the raw this
pointer. This means your object is not destroyed until it is registered as a listener in at least one callback.
An alternative is to derive all of your listeners from the same abstract base class, say Listener, and use pointers to Listener instead of function pointers.
class Listener
{
public:
virtual void callback() = 0;
};
class A {
private:
std::shared_ptr<Listener> listener; // or another smart pointer
public:
void setOnConfirmListener(std::shared_ptr<Listener> listener);
};
class B : public Listener {
private:
A a;
public:
B();
void foo();
void callback() { foo(); }
};
The downside is the necessity to derive all of your listeners from the same base class. Also, if the same class needs to have more than one listener callback, you have to jump through some hoops.
Upvotes: 4