1

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.

caps
  • 1,225
  • 14
  • 24
  • How should the compiler check that the argument types are compatible with the function parameter types? How shall it insert conversions from the argument types to the function parameter types? – dyp Dec 20 '14 at 19:39
  • I don't know. Answering that is part of answering the question, isn't it? The intended use would be that at both compiletime and runtime the argument types match the parameter types. – caps Dec 20 '14 at 19:42
  • As far as I know, it's impossible for arbitrary sets of types. The moment you store something in a `boost::function`, you erase all information about it (for the outside) and replace it with a single interface (return value + parameter types). `boost::function` seems not to support the ellipsis either. You could work around that issue by adding something like a `void*` to the function signature of `boost::function`, and then passing the additional parameters via this pointer. But still, this does not include neither type checks nor conversions. – dyp Dec 20 '14 at 19:48
  • In that case, what I desire is not obtainable via `boost::function` directly. Are there other possible implementations I could try? – caps Dec 20 '14 at 19:51
  • @caps Lemme make sure I understand correctly. You have `S` is some function that takes, say, 4 arguments... and you want to bind the first two, and have the result be a function that takes 2 arguments and calls `the_operation` with the 2 bound ones and the two passed ones? – Barry Dec 20 '14 at 19:52
  • @Barry That's right. – caps Dec 20 '14 at 19:54
  • I might have misunderstood you. I thought you wanted a fixed signature `S` for `boost::function Operate` such that you can store various functions in it that have different signatures (like `void(SomeClass*, some_type, int, bool)` and `void(SomeClass*, some_type, double, double, bool)`; and any additional arguments passed to `Operate` should be forwarded to the stored function. – dyp Dec 20 '14 at 19:56
  • @dyp That may or may not be necessary for the solution to the problem. From where I set I don't think `Operate` will require an invariant signature across all instantiations of `FunctionHandler`, unless there is a special invariant signature that would do what I want. I am not sure. That is (part of) why I asked the question. A possible solution might involve `Operate` having a variant signature deduced from `S`. EDIT: No. *Within a single instantiation of `FunctionHandler`*, `Operate`'s signature would be invariant. – caps Dec 20 '14 at 20:01
  • Let me rephrase my doubts: Would it be sufficient to get the list of parameter types from `S` (the template parameter of `FunctionHandler`), then prepend the two types `SomeClass*, std::back_insert_iterator >` to that list, and form the signature `???` from this new list and the return type in `S`? – dyp Dec 20 '14 at 20:06
  • @dyp That sounds like a possible solution, yes. However, I believe that `SomeClass*` and `std::back_insert_iterator >` will be absent from `???`, where they will be part of `S`. In other words, `FunctionHandler` will require `S` to have those two as its first two parameters, but they will not be parameters on `Operate`, which serves as the public interface for `member_function`. Let me know if that is not clear on re-reading the question--I may need to edit it. – caps Dec 20 '14 at 20:10
  • Ah, yes, of course. So you need to remove the first two parameter types from `S` to get `???`? – dyp Dec 20 '14 at 20:11

3 Answers3

2

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Do I need to use `boost::result_of` here? Couldn't I have the return type as another parameter of the template? – caps Dec 20 '14 at 20:37
  • @caps It'd be redundant. The return type is part of `S`. – Barry Dec 20 '14 at 20:40
  • Yes, but if I pass the return type on its own I don't need to use `boost::result_of`. "Using boost" is something my co-workers prefer to do as little as possible, for a variety of good and bad reasons. I'll probably float the `result_of` solution first, I just need to know if it is possible to get by without it. – caps Dec 20 '14 at 20:45
  • @caps You're already using boost though. Can you use C++11? I can give you a non-boost C++11 solution :) – Barry Dec 20 '14 at 20:46
  • No, for our purposes C++11 is not really available from our IDE. We do use `boost`, but our approach is "use `boost` when you *need* it." – caps Dec 20 '14 at 20:47
  • Two notes: 1) could you leave the other answer up for posterity? It might be more preferable for some users in the future, for reasons specific to their own use case. Although I like this answer better. 2) I am getting "error: declaration of 'operator()' as non-function" when I try to compile this on [coliru](http://coliru.stacked-crooked.com/a/ba7957c28af6701f). – caps Dec 20 '14 at 20:50
  • @caps (1) I like this one better, but sure. That one you actually have to do `using namespace boost;` which is even worse than just having to use it :) (2) Fixed! Really silly error on my part. – Barry Dec 20 '14 at 20:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/67389/discussion-between-caps-and-barry). – caps Dec 20 '14 at 21:09
  • To get rid of the repeated `boost::result_of` for each overload, you could use my technique to remove the first two parameters of `S`. Then, you need only one typedef: `typedef typename boost::function_types::result_type::type>::type result_type;` – dyp Dec 20 '14 at 23:07
  • @dyp I tried that. coliru complained about the syntax you posted there and I didn't want to spend time trying to figure it out. Barry's solution worked for me. – caps Dec 21 '14 at 04:28
1

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...);
    }
};
Barry
  • 286,269
  • 29
  • 621
  • 977
1

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

dyp
  • 38,334
  • 13
  • 112
  • 177
  • This gives you the type, but how would you construct it? – Barry Dec 20 '14 at 20:48
  • Yes, this lets you define `Operate` as `boost::function > Operate` which, while cool, doesn't show how to construct `Operate` from `member_function`. I'll give this one an upvote though, since I suspect it could be useful to readers in the future. – caps Dec 20 '14 at 20:59
  • @caps d'oh, I've done this for C++11 already, but I cannot find a decent way in C++03 to do it - only with techniques to mimic variadic templates (similar to Barry's first answer). It can be simplified a bit by using `boost::arg` instead of the named placeholders, but the manual overloading or specialization remains, unless you use macros. – dyp Dec 20 '14 at 23:04