c++implicit-conversionc++03boost-bindboost-function

automatic conversion from boost::bind_t to boost::function


I have a method of the following signature:

template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      boost::function<T(pmt::pmt_t)> converter,
      boost::function<void(T)> action)

(pmt_t is a complete type, before you ask)

as well as overloads that take T converter(pmt::pmt_t) and void converter(T) (i.e. raw C/C++ functions), as well as all permutations of the above boost::function<> and the C-style function arguments. That leaves me with 4 different methods already.

I'd like to avoid increasing the number of methods further. However, the most common thing I'll do is call something like

register_msg_action(pmt::mp("key"),
    pmt::to_long, /* "raw" function long(pmt_t) */
    boost::bind(&my_class::void_method_of_long, this, _1) /* CAVEAT */
);

My approach was that the /* CAVEAT */ argument is implicitly convertible to boost::function<void(T)>, but however, this doesn't seem to be the case (g++ 5.1.1):

error: no matching function for call to ‘register_msg_action(pmt::pmt_t, boost::function<long int(boost::intrusive_ptr<pmt::pmt_base>)>&, boost::_bi::bind_t<void, void (*)(long int), boost::_bi::list1<boost::arg<1> > >)’
     register_msg_action(pmt::mp("hi"), long_function, boost::bind(&my_class::void_method_of_long, this ,_1));

... all the other candidates (boost::function,boost::function); (T(pmt_t),boost::function); (T(pmt_t), void(T)) ...

test.cc:56:1: note: candidate: template<class T> void register_msg_action(const pmt_t&, T (*)(pmt::pmt_t), boost::function<void(T)>)
 register_msg_action(const pmt::pmt_t& name,
 ^
test.cc:56:1: note:   template argument deduction/substitution failed:
test.cc:80:76: note:   ‘boost::_bi::bind_t<void, void (*)(long int), boost::_bi::list1<boost::arg<1> > >’ is not derived from ‘boost::function<void(T)>’
     register_msg_action(pmt::mp("key"), pmt::to_long, boost::bind(&my_class::void_method_of_long, this, _1));

Now, doing

boost::function<void(long)> action (boost::bind(&my_class::void_method_of_long, this, _1));
register_msg_action(pmt::mp("key"), pmt::to_long, action);

works beautifully. Since there is even a constructor that takes boost::_bi::bind_t in boost::function, I wonder what I must do to make this work, without

I'm afraid of adding the type of the third argument as additional template typename, because that would break the parameter list type safety that is necessary to guarantee action(converter(pmt::pmt_t)) works, and honestly, I'd rather deal with more code now than examine user's templated g++ errors later on.


Solution

  • The problem is when T appears in the signature of register_msg_action within template arguments of boost::function. Then, if you're not calling it with an actual boost::function object, it cannot be deduced. It should work if you specify the template argument explicitly:

    register_msg_action<long>(pmt::mp("key"),
        pmt::to_long, /* "raw" function long(pmt_t) */
        boost::bind(&my_class::void_method_of_long, this, _1)
    );
    

    If you want to keep the option of deducing T when using at least one plain-function argument, you have the option to explicitly make the T non-deducible in its boost::function use:

    template <class T>
    struct NonDeduced
    {
      typedef T type;
    };
    
    // T has to be specified explicitly at call site
    template<typename T>
    void
    register_msg_action(const pmt::pmt_t& name,
          boost::function<typename NonDeduced<T>::type (pmt::pmt_t)> converter,
          boost::function<void(typename NonDeduced<T>::type)> action)
    
    // T deducible from converter
    template<typename T>
    void
    register_msg_action(const pmt::pmt_t& name,
          T converter(pmt::pmt_t),
          boost::function<void(typename NonDeduced<T>::type)> action)
    
    // T deducible from action
    template<typename T>
    void
    register_msg_action(const pmt::pmt_t& name,
          boost::function<typename NonDeduced<T>::type (pmt::pmt_t)> converter,
          void action(T))
    
    // T deducible from both, must match
    template<typename T>
    void
    register_msg_action(const pmt::pmt_t& name,
          T converter(pmt::pmt_t),
          void action(T))
    

    [Live example]