Search code examples
c++boostboost-bind

Why does this boost bind non-static member function fail?


Why does the following compiles i.e. passing a free function as parameter with the right signature:

inline double free_adapter_f(unsigned n, const double *x, double *grad, void *d) {
    return 0.0;
}

nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());
opt.set_min_objective(free_adapter_f, NULL);

whereas this other doesn't compile i.e. passing the result of boost::bind a class member function with the same signature:

template<class Space, class Solution, class Oracle>
inline double NelderMead<Space, Solution, Oracle>::adapter_f(unsigned n, const double *x, double *grad, void *d) {
    return 0.0;
}

nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());      
opt.set_min_objective(boost::bind(&NelderMead::adapter_f, this, ::_1, ::_2, ::_3, ::_4), NULL);

The error message is the following:

nelder_mead.h(98): error: no instance of overloaded function "nlopt::opt::set_min_objective" matches the argument list
    argument types are: (boost::_bi::bind_t<double, boost::_mfi::mf4<double,     NelderMead<TestSpace, VectorXd, oracle_f>, unsigned int, const double *, double *, void *>, boost::_bi::list5<boost::_bi::value<NelderMead<TestSpace, VectorXd, oracle_f> *>, boost::arg<1>, boost::arg<2>, boost::arg<3>, boost::arg<4>>>, long)
        object type is: nlopt::opt
opt.set_min_objective(boost::bind(&NelderMead::adapter_f, this, ::_1, ::_2, ::_3, ::_4), NULL);

UPDATE: the overloaded set_min_objective are:

 typedef double (*func)(unsigned n, const double *x, double *grad, void *f_data);
 typedef double (*vfunc)(const std::vector<double> &x, std::vector<double> &grad, void *f_data);
 void set_min_objective(func f, void *f_data);
 void set_min_objective(vfunc vf, void *f_data);

Solution

  • Here is a simple example, which demonstrates your problem:

    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    
    namespace g
    {
      typedef int (*func)(int a, int b, int c);
    
      void bar(func f)
      {
        std::cout << "g::bar:: called" << (*f)(10, 20, 30) << std::endl;
      }
    
      // Disable the over load below and you will get the same error
      void bar(boost::function<int(int, int, int)> f)
      {
        std::cout << "g::bar:: called" << f(10, 20, 30) << std::endl;
      } 
    }
    
    template <typename A, typename B, typename C>
    class foo
    {
    
    public:
    
      int bar(int a, int b, int c) const
      { return a + b + c; }
    
      void call()
      {
        g::bar(boost::bind(&foo::bar, this, ::_1, ::_2, ::_3));
      }
    };
    
    int main(void)
    {
      foo<int, double, int> f;
      f.call();
      return 0;
    }
    

    Main reason is that boost::function<> is not convertible to a function pointer, so you need to provide an overload which accepts this (as above.)

    EDIT: just to clarify things a little further. boost::bind() does not explicitly return a boost::function<>, however, the object it returns can be stored in the correct instantiation of boost::function<>, in the above case, the correct instantiation is boost::function<int(int, int, int)>.

    Normally you would only need to resort to storing it in a boost::function if you were interested in propagating it (without a template) or storing it for later use. In this case, as you are passing the result of the bind(), you need to have the correct overload to accept the returned object from boost::bind(), and the easiest way to do this without resorting to templates is to accept a boost::function (as above.)

    Normally, I'm pragmatic, so I would resort to this (without knowing what you are wanting to do with f) where possible.

    template <typename F>
    double set_min_objective(F f, ...)
    {
    }
    

    Then you are agnostic, of course purists will have other opinions.

    NOTE: A nice thing with boost::function<> is that you can store a non-member function pointer in one too (as long as the signature matches.) So in reality you only need a version of your function which accepts the correct boost::function<> and it will work in both cases (member function with boost::bind() and non-member function.)

    EDIT2: Okay, given the additional information, you have to resort to the following mechanism, you need to have a non-member function of your class, which will then delegate to the member function, for example:

    <>
    class NelderMead
    {
    
    static double delegate_f(unsigned n, const double *x, double *grad, void *f_data)
    {
      // I'm assuming here the frame work passed you whatever you gave in f_data
      NelderMead* inst = reinterpret_cast<NelderMead*>(f_data);
      return inst->adapter_f(n, x, grad);
    }
    
    double adapter_f(unsigned n, const double *x, double *grad)
    {
    }
    
    void set()
    {
      nlopt::opt opt(nlopt::LN_NELDERMEAD, X.size());      
      opt.set_min_objective(delegate_f, this); //<-- here pass the instance as the additional data
    }
    
    };
    

    This is a typical pattern employed by third-party libraries which are meant to be agnostic to user code.