Search code examples
c++enable-ifboost-signals2

boost::signal2 bind to pure or member function using enable_if


I am currently trying to implement a member function of a class which allows for setting a callback that is either itself a member function (of a potentially different class) or a pure function not being part of a class.

Given such a class,

#include <boost/signals2.hpp> // for callback
class CallbackSetter
{ 
  template <typename T>
  void setCallback(T &p_rCallback)
  {
     m_oCallback.connect(boost::bind(&p_rCallback, _1);
  }
  boost::signals2::signal<void(const std::string &p_rTLName)> m_oCallback; 
}


CallbackSetter oSetter;
oSetter.setCallback(theFunction);

works fine for non-member methods. However I am sort of failing implementing something that lets the user connect to either a member of not. This is what I tried:

class CallbackSetter
{ 
  template <typename T, typename Cl, 
    typename std::enable_if_t<!std::is_class<Cl>::value>>
  void setCallback(T &p_rCallback, Cl & p_rCallbackclass)
  {
     m_oCallback.connect(boost::bind(&p_rCallback, p_rCallbackclass, _1);
  }

  template <typename T, typename Cl,
    typename std::enable_if_t<std::is_class<Cl>::value> >
  void setSignalChangeCallback(T &p_rCallback, Cl & p_rCaller)
  {
    m_oObserverCallback.connect(boost::bind(&p_rCallback, p_rCaller, _1));
  }
}

called by some other classes' member function like this:

#include <string>
class OtherClass
{
  void caller()
  {
    m_oMyCS.setCallback(&OtherClass::executeMe, this);
  }
  void executeMe(std::string &p_rTag)
  {
    // do whatever with p_rTag
  }

  CallbackSetter m_oMyCS;
}

and finally

void noMemberExec(std::string &p_rTag)
{
  // do whatever
}

int main()
{
  OtherClass oOC;
  oOC.caller();
  CallbackSetter oCS;
  oCS.setCallback(&noMemberExec, nullptr);
  return 0;
}

Any help here is welcome!

P.S.: I am using VS 2015, which quits with an error

C2783: error calling 'setCallback(T&, CL&)': could not deduce template argunemt for '__formal'

Solution

  • Instead of having different overloads for setCallback() and trying to account for different situations - just provide one:

    template <class F>
    void setCallback(F f)
    {
        m_oCallback.connect(f);
    }
    

    It's just up to the user to provide a function that can be called with a std::string const&. If I want it to call a free function taking a single argument, I just do that:

    oCS.setCallback(noMemberExec);
    

    if I want it to call a member function on a class instance, I do it explicitly:

    m_oMyCS.setCallback([this](std::string const& arg){ executeMe(arg); });
    

    Either way, I'm passing a single-argument callable into setCallback().