Search code examples
c++templatesboostc++03boost-bind

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


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.


Solution

  • 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.