3

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.

gimmeamilk
  • 2,016
  • 5
  • 24
  • 36
  • Where would you get the string, int, some-class, and int, for example, from? My `stream_function` (found [here](http://stackoverflow.com/a/8478039/500104)) might help. – Xeo Sep 17 '13 at 10:40
  • Edited to make deserializer usage more clear. The deserializer itself is blackbox code. – gimmeamilk Sep 17 '13 at 10:49

2 Answers2

6

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.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
2

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

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • Note that this has the same problem as my `stream_function` had - the evaluation order of the `get_value` calls is unspecified. – Xeo Sep 17 '13 at 11:03
  • Yes, this is almost exactly what I'm looking for, but I'm finding the arguments appear to be being passed in reverse order. Is there a way to ensure the evaluation order? – gimmeamilk Sep 17 '13 at 11:14
  • @gimmeamilk: See my answer which I linked in my earlier comment, if you're using Clang. If you're using GCC, you'll need to manually build up the pack and call one function at a time, recursively. – Xeo Sep 17 '13 at 14:29
  • @Xeo it is gcc unfortunately. I'm unsure what the recursive solution would be. I can call each get_value() one at a time on top of recursion, but how would I pass all the values to the callback at the same time? – gimmeamilk Sep 17 '13 at 15:14
  • 2
    @gimmeamilk: You would aggregate them in a variadic pack. [Here](http://coliru.stacked-crooked.com/a/d7f72f88619ebd0c) is an example. – Xeo Sep 17 '13 at 15:53
  • @Xeo you're a genius! I don't really have much idea what this code is doing at all (will take a little time to digest it I think..) but it seems to work perfectly. Thanks a bunch. – gimmeamilk Sep 17 '13 at 16:05
  • @gimmeamilk: `types` is just a so-called "tag-type" that allows me to do two things: I can see when the pack is empty (`types<>`) and I can can process the `Args` in a head-then-tail kind-of recursive fashion. – Xeo Sep 17 '13 at 16:17
  • @Xeo good example. Please add your own answer with this code and then I will delete my answer. – ForEveR Sep 18 '13 at 05:52