Using boost::bind but allowing any additional parameters to be passed through

109 Views Asked by At

I am putting together a "simple" template class. It offers an interface for performing some operations on a database, so there are other members as well (primarily for operating on the container member). However, for our purposes, the template class looks something like this:

template<typename T, //the type of objects the class will be manipulating
         typename S> //the signature of the function the class will be using
FunctionHandler
{
private:
    std::vector<T> container;
    boost::function<S> the_operation;
    SomeClass* pSC; //a database connection; implementation unimportant

    //some other members--not relevant here
public:
    boost::function<???> Operate;
    FunctionHandler(boost::function<S> the_operation_)
        : the_operation(the_operation_)
    {
        Operate = boost::bind(the_operation, pSC, std::back_inserter<std::vector<T> >,
                              /*infer that all other parameters passed to Operate
                                should be passed through to the_operation*/);
    }

    //other peripheral functions
}

My question is two-fold.

  1. What do I put as the template parameter for Operate. i.e. what replaces ???
  2. How do I tell boost::bind that it should pass any other parameters given to Operate on to the_operation? In other words, for some arbitrary function signature S that looks like void (SomeClass*, std::back_insert_iterator<std::vector<T> >, int, bool) and some other arbitrary function signature O that looks like void (SomeClass*, std::back_insert_iterator<std::vector<T> >, double, double, bool) how do I write this template class such that Operate has a signature of void (int, bool) for the first and void (double, double, bool) for the second, and passes its values on to the_operation's 3rd-Nth parameters?

In my searches I couldn't find any questions quite like this one.

3

There are 3 best solutions below

10
On BEST ANSWER

Why even use bind? We can get the same effect without it. I'm using Iter as a template but you can fill it in with whater the right type is:

template <typename S, typename Iter>
class Operator
{
    boost::function<S> func_;
    SomeClass* cls_;
    Iter iter_;

public:
    Operator(function<S> func, SomeClass* cls, Iter iter)
    : func_(func), cls_(cls), iter_(iter)
    { }

    // one for each # of args
    typename boost::result_of<
        boost::function<S>(SomeClass*, Iter)
    >::type operator()() const {
        return func_(cls_, iter_);
    }

    template <typename A>
    typename boost::result_of<
        boost::function<S>(SomeClass*, Iter, A)
    >::type operator()(A a) const {
        return func_(cls_, iter_, a);
    }

    template <typename A, typename B>
    typename boost::result_of<
        boost::function<S>(SomeClass*, Iter, A, B)
    >::type operator()(A a, B b) const {
        return func_(cls_, iter_, a, b);
    }

    // etc.
};

We're making all of the operator()s, but they'll only get instantiated if they get called - so as long as you call the right one (which for any solution, you'll have to anyway), this works.

0
On

Unfortunately there's no way to "infer" all the rest of the arguments. You have to specify all the correct placeholders. Since C++03, we can just use lots of template specializations.

template <typename S> struct Operate;

template <typename R, typename Iter>
struct Operate<R(SomeClass*, Iter)>
{
    using namespace boost;

    function<R()> op_;

    Operator(function<R(SomeClass*, Iter)> op, SomeClass* cls, Iter iter)
    : op_(bind(op, cls, iter))
    { }
};

template <typename R, typename Iter, typename A>
struct Operate<R(SomeClass*, Iter, A)>
{
    using namespace boost;

    function<R(A)> op_;

    Operator(function<R(SomeClass*, Iter, A)> op, SomeClass* cls, Iter iter)
    : op_(bind(op, cls, iter, _1))
    { }
};

template <typename R, typename Iter, typename A, typename B>
struct Operate<R(SomeClass*, Iter, A, B)>
{
    using namespace boost;

    function<R(A, B)> op_;

    Operator(function<R(SomeClass*, Iter, A, B)> op, SomeClass* cls, Iter iter)
    : op_(bind(op, cls, iter, _1, _2))
    { }
};

// etc.

It's verbose, but if you can't use C++11, I don't know what else you could do. Which, for completeness:

template <typename R, typename Iter, typename... Extra>
struct Operator<R(SomeClass*, Iter, Extra...)>
{
    std::function<R(SomeClass*, Iter, Extra...)> op_;
    SomeClass* cls_;
    Iter iter_;

    Operator(function<R(SomeClass*, Iter, Extra...)> op, SomeClass* cls, Iter iter)
    : op_(op), cls_(cls), iter_(iter)
    { }

    R operator()(Extra... args) const {
        return op_(cls_, iter_, args...);
    }
};
3
On

My knowledge of boost.MPL is unfortunately quite limited, so I don't think this is the nicest way to solve the issue of removing the first two parameter types from a function type.

#include <boost/function_types/components.hpp>
#include <boost/function_types/function_type.hpp>
#include <boost/mpl/erase.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/int.hpp>

template<typename F, int N>
class remove_first_N_param_types
{
        typedef typename boost::function_types::components<F>::type
    components;
        typedef typename boost::mpl::begin<components>::type
    beg;
        typedef typename boost::mpl::advance<beg, boost::mpl::int_<1  >>::type
    beg_param;
        typedef typename boost::mpl::advance<beg, boost::mpl::int_<1+N>>::type
    beg_param_plus_N;
        typedef typename boost::mpl::erase<components,
                                           beg_param, beg_param_plus_N>::type
    erased_first_N_params;

public:
        typedef typename boost::function_types::
        function_type<erased_first_N_params>::type
    type;
};

Live example