Posts RSS Comments RSS Del.icio.us Digg Technorati Blinklist Furl reddit 68 Posts and Comments till now
This wordpress theme is downloaded from wordpress themes website.

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();
}

Trackback this post | Feed on Comments to this post

Leave a Reply