C ++: Store a pointer to a member function of an object in another object
I have a class that calls a user-specified function in certain cases. Therefore, the class has a method void setExternalPostPaintFunction(void(*function)(QPainter&));
that can be used to "register" a function. Then this function will be called:
class A {
public:
void setExternalPostPaintFunction(void(*function)(QPainter&));
private:
void (*_externalPostPaint)(QPainter&);
bool _externalPostPaintFunctionAssigned;
};
The function pointer is stored in a member variable _externalPostPaint
. The implementation setExternalPostPaintFunction
looks like this:
void A::setExternalPostPaintFunction(void(*function)(QPainter&)) {
_externalPostPaint = function;
_externalPostPaintFunctionAssigned = true;
}
This now works with normal functions. However, I also want to be able to pass pointers to member functions of objects. From what I know, I also need to pass and store a pointer to an object in this case. However, I don't know what type the other object will be. So I guess I am forced to use templates. I already thought about something like this:
class A {
public:
template <typename T>
void setExternalPostPaintFunction(void(T::*function)(QPainter&), T* object);
private:
void (T::*_externalPostPaint)(QPainter&); //<- This can't work!
bool _externalPostPaintFunctionAssigned;
};
This way I can pass a function pointer and an object pointer setExternalPostPaintFunction
and can probably call a function on an object inside that function. But I cannot store it in a variable _externalPostPaint
because the type T
is inferred only when the function is called setExternalPostPaintFunction
, so I cannot have a member variable that depends on that type, since the type of my member variable must be known when the object is created, and besides Moreover, it cannot change, but it will occur when a new function is assigned, which may be a member function of an object of a different type.
So what's the correct way to do this, or not? I'm not very good with templates and function pointers, so I might be missing something.
The Anoter parameter should certainly have created a functor class with a virtual member function that can be overwritten in the derived class and then pass + store a pointer to an object of that type instead of a function pointer. But I would somehow prefer my approach if possible.
EDIT: SOLUTION
Tartan Llama got me on the right track by suggesting to use std::function
. This is how I solved it:
class A {
public:
template <typename T>
void setExternalPostPaintFunction(T* object, void(T::*function)(QPainter&)) {
_externalPostPaint = std::bind(function, object, std::placeholders::_1);
_externalPostPaintFunctionAssigned = true;
}
void setExternalPostPaintFunction(std::function<void(QPainter&)> const& function);
private:
std::function<void(QPainter&)> _externalPostPaint;
bool _externalPostPaintFunctionAssigned;
};
As you can see, the function / member pointer is now stored in the object std::function<void(QPainter&)>
. The advantage is that it std::function
can basically store any callable target. Then there are two overloads: one that can be used on any object std::function
that also accepts, eg. a normal function pointer (because the expected one is then std::function
implicitly constructed) and one for member functions to be called on the object (more for convenience). The latter is implemented as a template. This uses std::bind
to std::function
instantiate a call to this member function (user passed) on an object (user passed).
The overload that takes std::function
is implemented in the source file as follows:
void ImageView::setExternalPostPaintFunction(std::function<void(QPainter&)> const& function) {
_externalPostPaint = function;
_externalPostPaintFunctionAssigned = true;
}
Calling this stored function in class code is A
now simple:
//canvas is a QPainter instance
if (_externalPostPaintFunctionAssigned) _externalPostPaint(canvas);
A user who wants to register a member function as a callback function should do the following:
//_imageView is an instance of "A"
//"MainInterface" is the type of "this"
_imageView->setExternalPostPaintFunction(this, &MainInterface::infoPaintFunction);
Or, if it's not a member function, but just a normal function:
void someFunction(QPainter& painter) {
//do stuff
}
_imageView->setExternalPostPaintFunction(&someFunction);
Or he can explicitly create an object std::function
and pass it:
std::function<void(QPainter&)> function = [&](QPainter& painter){ this->infoPaintFunction(painter); };
_imageView->setExternalPostPaintFunction(function);
Works like a charm.
source to share
You can use std::function
:
class A {
public:
//PostPaintFun can be anything which acts as a function taking a QPainter&
//Could be a lambda, function pointer, functor, etc.
using PostPaintFun = std::function<void(QPainter&)>;
void setExternalPostPaintFunction(PostPaintFun fun);
private:
//Names beginning with an underscore are reserved, don't use them
//Ending with an underscore is fine
PostPaintFun fun_;
bool externalPostPaintFunctionAssigned_;
};
Now you can use member functions like this:
struct B
{
void exec(QPainter&) const;
};
void foo() {
B b;
a.setExternalPostPaintFunction(
[b] (QPainter& p) {b.exec(p);}
);
}
//or inside B
void B::foo() {
a.setExternalPostPaintFunction(
[this] (QPainter&p) {this->exec(p);}
);
}
source to share
I have to say that I prefer Tartan Llama's answer, but here you have something that might work for you.
This may take some work, but I'm sure you get the idea.
struct IFunctionHolder {}; // Used for pointing to any FunctionHolder
typedef IFunctionHolder* functionHolder_ptr; // Alias for IFunctionHolder* .
template<typename Function> // The template for the actual function holders.
struct FunctionHolder: public IFunctionHolder
{
Function function;
};
class A {
public:
template <typename T>
void setExternalPostPaintFunction(void(T::*function)(QPainter&), T* object);
private:
functionHolder_ptr *function_holder; // This memeber can hold eny instantiation of template<> FunctionHolder.
// Instantiate this member wen calling setExternalPostPaintFunction
bool _externalPostPaintFunctionAssigned;
};
You might have code like this:
A some_a;
void some_a.setExternalPostPaintFunction(&SomeInstance::some_fnunction); // Here take place the instantiation of FunctionHolder.
some_a.function_holder.function(some_painter);
source to share