gimmeamilk
gimmeamilk

Reputation: 2120

C++ : create custom function dispatcher from variadic template

I have some functions that read various types from serialized data, eg:

class DataDeserializer
{
    int getInt();
    std::string getString();
    MyClass getMyClass();
}

I then have various callback functions that take arbitrary parameters, eg:

void callbackA (int, int, int);
void callbackB (int, std::string);
void callbackC (std::string, int, MyClass, int);

I want to call the various callbacks with arguments read from the deserialized data stream. What I would like is to automate the boilerplate code as much as possible. I was thinking maybe I could use templates. If I had some sort of Dispatcher class, eg:

template <SOMETHING??> class Dispatcher
{
    void dispatch()
    {
        // ???? 
    }

    SOMEFUNCTIONTYPE callback;
    DataDeserializer myDeserializer;
};

Then declare various specific dispatchers:

Dispatcher<int,int,int>                  myDispatcherA (deserializer, callbackA);
Dispatcher<int,std::string>              myDispatcherB (deserializer, callbackB);
Dispatcher<std::string,int,MyClass,int>  myDispatcherC (deserializer, callbackC);

Then when I want to dispatch, I just call:

myDispatcherB.dispatch();

which underneath would expand to something like this:

void dispatch()
{
    callback (myDeserializer.getString(), myDeserializer.getInt(), myDeserializer.getMyClass(), myDeserializer.getInt());
}

Is this possible with C++11 variadic templates? I've read up a little on them, and it seems recursion is used a lot.

Upvotes: 3

Views: 1944

Answers (2)

Xeo
Xeo

Reputation: 131907

I have done something similar for my stream_function class. The basic idea is that you pass a type to a function template, which does The Right Thing™, and expand that call:

callback(magic<Args>(/* sth */)...);

However, if your functions aren't pure and modify some state, and as such have the requirement that they need to be called in the correct order, you have to force that order with some tricks.

If you're using Clang, this is rather easy, as it forces left-to-right evaluation for braced-init-lists. This allows you to just use a small helper type

struct invoker{
  template<class F, class... Args>
  invoker(F&& f, Args&&... args){ f(std::forward<Args>(args)...); }
};

and then do

invoker{ callback, magic<Args>(/* sth */)... };

Unfortunately, GCC doesn't yet implement this feature, so one needs to resort to manual order-enforcement. This can be done with a small helper struct which is just a type-list, but allows one to do some useful things:

  • see when the pack is empty (types<>), and
  • process Args in a head-then-tail recursive fashion

template<class...> struct types{};

template<class... Args>
struct dispatcher{
    std::function<void(Args...)> f;

    void call(){ _call(types<Args...>{}); }
private:
    // take head, produce value from it, pass after other values
    template<class Head, class... Tail, class... Vs>
    void _call(types<Head, Tail...>, Vs&&... vs){
        _call(types<Tail...>{}, std::forward<Vs>(vs)..., get_value<Head>());
    }

    // no more values to produce, forward to callback function
    template<class... Vs>
    void _call(types<>, Vs&&... vs){ f(std::forward<Vs>(vs)...); }
};

Live example.

Upvotes: 6

ForEveR
ForEveR

Reputation: 55897

Something like this can help you

template<typename T>
T get_value(Deserializer&);

template<>
int get_value(Deserializer& d)
{
   return d.getInt();
}

template<>
std::string get_value(Deserializer& d)
{
   return d.getString();
}

template<typename... Args>
class Dispatcher
{
public:
   template<typename Functor>
   Dispatcher(Deserializer& d, const Functor& cb) : myDeserializer(d), callback(cb)
   {
   }
   void dispatch()
   {
      callback(get_value<Args>(myDeserializer)...);
   }
private:
   std::function<void(Args...)> callback;
   Deserializer myDeserializer;
};

Live example

Upvotes: 2

Related Questions