C++ member function pointer, parent class, template, std::function
Today I ran into a C++ scenario where I found std::function to be useful. I wanted to create a std::queue of member function pointers to functions that are children of the same parent class. For example:
class Parent
{
public:
Parent() {};
virtual ~Parent() {};
};
class ChildA : public Parent
{
public:
ChildA() : Parent() {};
~ChildA() {};
void Execute01() {printf("A");};
void Execute02() {printf("B");};
void Execute03() {printf("C");};
};
class ChildB : public Parent
{
public:
void Execute01() {printf("D");};
void Execute02() {printf("E");};
void Execute03() {printf("F");};
};
To keep it simple, this examples shows two child classes (ChildA, ChildB), but I wanted it to work for lots more child classes. I wanted to push() member function pointers to ChildA::Execute01() etc into a std::queue… But I ran into an issue. The declaration for a member function pointer requires the class to be specified, and the execution requires an instance of the class the be specified. So the following snippet is valid:
typedef void (ChildA::*funcPtr)(); std::queue<funcPtr> theQueue; ChildA childA; theQueue.push(&ChildA::Execute01); funcPtr fn = theQueue.front(); (childA.*fn)();
However, I can’t push() a pointer to a ChildB member function into this same std::queue. If I try referencing the parent class as follows:
typedef void (Parent::*funcPtr)(); std::queue<funcPtr> theQueue; ChildA childA; theQueue.push(&ChildA::Execute01); funcPtr fn = theQueue.front(); (childA.*fn)();
Then Visual Studio 2013 gives me an error:
Error 9 error C2664: ‘void std::queue<funcPtr,std::deque<_Ty,std::allocator<_Ty>>>::push(void (__cdecl Parent::* const &)(void))’ : cannot convert argument 1 from ‘void (__cdecl ChildB::* )(void)’ to ‘void (__cdecl Parent::* &&)(void)’
Ignoring C++14’s variable templates, there are two types of templates – function templates and class templates… So we can’t just use a template on the function pointer’s typedef. Instead, my next solution was to make a queue of Delegate’s and use a template to define the delegate class’s children:
class Delegate
{
public:
Delegate() {};
virtual ~Delegate() {};
virtual void ExeFunc() = 0;
};
template<class T>
class DelegateT : public Delegate
{
public:
DelegateT(T* obj, void (T::*func)()) { m_obj = obj; m_func = func; };
~DelegateT() {};
void ExeFunc() { (m_obj->*m_func)(); }
protected:
void (T::*m_func)();
T* m_obj;
};
And here’s an example usage:
std::queue<Delegate *> theQueue;
ChildA childA;
ChildB childB;
theQueue.push(new DelegateT<ChildA>(&childA, &ChildA::Execute01));
theQueue.push(new DelegateT<ChildB>(&childB, &ChildB::Execute01));
while (!theQueue.empty())
{
Delegate *del = theQueue.front();
del->ExeFunc();
theQueue.pop();
delete del;
}
Finally I ran into a more modern solution (but not that modern seeing as it’s supported by Visual Studio 2010) – use std::function:
ChildA childA;
ChildB childB;
std::queue<std::function<void()>> theQueue;
theQueue.push(std::bind(&ChildA::Execute01, childA));
theQueue.push(std::bind(&ChildB::Execute01, childB));
while (!theQueue.empty())
{
std::function<void()> func = theQueue.front();
func();
theQueue.pop();
}
mepem37 :: 2015/10/06 (Tuesday, October 6, 2015) :: C++, Programming & Computer Science :: No Comments »
Del.icio.us
Digg
Technorati
Blinklist
Furl
reddit