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 »