1

I am currently reworking my callback system to C++11 variadic templates. Basically what I am trying to do is to store a function with a return value and any number of arguments.

Afterwards this function should be called and parameters are loaded from a boost::archive and return value should be written back to another archive.

#include <string>
#include <map>
#include <iostream>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

using iarchive = boost::archive::binary_iarchive;
using oarchive = boost::archive::binary_oarchive;
using namespace std;

template<class Interface>
class RFCProvider : public Interface {
    class FuncStore {
    public:
        virtual ~FuncStore() {}
        virtual void call(iarchive& in, oarchive& out) = 0;
    protected:
        template <class T> T read(iarchive& in) { T a; in >> a; return a; }
    };


    template<class Return, class... Args>
    class FuncStoreRetN : public FuncStore {
        public:
            typedef Return(Interface::*Function)(Args... args);
            typedef Return ReturnType;
            static const int ArgCount = sizeof...(Args);
        private:
            struct pass {
                Return res;
                pass(Interface* me, Function func, Args... args) { res = (me->*func)(args...); }
            };

            Interface* mMe;
            Function mFunc;
        public:
            FuncStoreRetN(Interface* me, Function func) : mMe(me), mFunc(func) {}
            void call(iarchive& in, oarchive& out) {
                pass c{mMe, mFunc, read<Args>(in)...};
                out << c.res;
            }
    };


    map<string, FuncStore*> functionStore;
public:
    template<class Return, class... Args>
    void registerFunction(const string& name, Return(Interface::*func)(Args...)) {
        functionStore[name] = new FuncStoreRetN<Return, Args...>(this, func);
    }
};


class TestClass {
public:
    bool foo(int x) { return x == 0; }
};


int main() {
    RFCProvider<TestClass> p;
    p.registerFunction("foo", &TestClass::foo);

    return 0;
}

From my understanding pass c{mMe, mFunc, read<Args>(in)...}; should expand to pass c{mMe, mFunc, read<int>(in), read<string>(in), ...}; am I right?

But I cant compile with g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2

error: expected primary-expression before ‘>’ token
     pass c{mMe, mFunc, read<Args>(in)...};
                                 ^

Any suggestions?

EDIT: added MCVE

WaeCo
  • 1,155
  • 1
  • 10
  • 21
  • Why? What's the purpose of having a bunch of functions that don't have the same signature n the same place? You can't call them from the same place, because you need to know the arguments there! – Mats Petersson Mar 06 '15 at 09:28
  • Please supply an [MCVE](https://stackoverflow.com/help/mcve). – Wintermute Mar 06 '15 at 09:34
  • The use case is a remote function call protocol where i call these stored functions via network. These stored functions are associated with a name and stored in a map. The arguments should be stored during compilation in the templates. – WaeCo Mar 06 '15 at 09:34
  • Does `this->read(in)...` solve the problem ? (maybe also add template keyword). – Jarod42 Mar 06 '15 at 09:36
  • What I can see so far is that `Args&&...` in the `Function` typedef should be `Args...`, but that should give you a different error. – Wintermute Mar 06 '15 at 09:37
  • I think the problem is that read expecting a single type to replace T, while you pass it possibly multiple types. Either way, read will not be called on each type unpacked as I guess is your intention. You have to do this recursively as a type list implementation. –  Mar 06 '15 at 09:39
  • `read(in)...` is a pack expansion, so that would actually work. It'd give funny results because the order of execution in the resulting parameter list to the `pass` constructor is undefined, but it would compile. – Wintermute Mar 06 '15 at 09:44
  • I hate to insist, but I am pretty sure, that pack expansion does not work that way. http://stackoverflow.com/questions/25680461/variadic-template-pack-expansion –  Mar 06 '15 at 09:48
  • That's not the same context. The OP in that question is attempting to build a comma-operator expression, not a function argument list like in this question. Anyway, I tried it, and both g++ and clang++ eat it. – Wintermute Mar 06 '15 at 09:51
  • Yes right, the comma-operator expression should preserve strict left to right order. I updated the code to my use case. – WaeCo Mar 06 '15 at 10:03
  • lovely, learned something today. thanks –  Mar 06 '15 at 10:20

1 Answers1

0

Replace

pass c{mMe, mFunc, read<Args>(in)...};

with

pass c{mMe, mFunc, FuncStore::template read<Args>(in)...};

Since all this happens with nested classes (and class templates) in a class template, dependent name lookup applies, and you have to add the usual clarifications for the compiler.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • According to [Wikipedia](http://en.wikipedia.org/wiki/Variadic_template) list initializer solves that problem of unspecified order. – WaeCo Mar 06 '15 at 11:51
  • Oh, you are correct. I did not know that. For reference, it is defined in [dcl.init.list]/4 (8.5.4 in C++11): "Within the *initializer-list* of a *braced-init-list*, the *initializer-clauses*, including any that result from pack expansions, are evaluated in the order in which they appear." I shall remove the offending paragraph forthwith. – Wintermute Mar 06 '15 at 12:24