解压缩存储为数组的可变模板函数调用数据(目标:RPC)

时间:2015-09-21 19:04:42

标签: c++ function templates args variadic

我们的想法是创建以下功能(看起来很简单)

void test(int , float , char* ){ /*gets called*/ } 

void main()
{
    RegisterRPC( test  ,  int , float , char* )
}

注册函数的伪代码:

std::map<std::string , std::function<void()> > functionarray;

template<typename F, typename... Args>
void RegisterRPC( F , Args )
{
    // somehow add to functionarray
}

然后,当数据来自网络时,需要将数据分解为使用适当的args进行呼叫测试。

ProcessData(data)
{
    data.begin();
    functionarray[data.get<char*>()] ( 
        data.get<int>() , 
        data.get<float>() , 
        data.get<char*>() ); // the RegisterRPC parameters
}

我已经发现Variadic模板可以存储args

expanded parameter list for variadic template

它可以将args转化为类

How can I iterate over a packed variadic template argument list?

所以我相信它可能 - 只是我不知道如何...... 希望有人可以提供帮助。

编辑:如果有人对完整的解决方案感兴趣:

#include <tuple>
#include <iostream> 
#include <strstream>
#include <istream>
#include <sstream>
#include <string>

// ------------- UTILITY---------------
template<int...> struct index_tuple{};

template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl;

template<int I, int... Indexes, typename T, typename ... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...>
{
    typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};

template<int I, int... Indexes>
struct make_indexes_impl<I, index_tuple<Indexes...> >
{
    typedef index_tuple<Indexes...> type;
};

template<typename ... Types>
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...>
{};

// ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------

using namespace std;

template<class Ret, class... Args, int... Indexes >
Ret apply_helper(Ret(*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
{
    return pf(forward<Args>(get<Indexes>(tup))...);
}

template<class Ret, class ... Args>
Ret apply(Ret(*pf)(Args...), const tuple<Args...>&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}

template<class Ret, class ... Args>
Ret apply(Ret(*pf)(Args...), tuple<Args...>&&  tup)
{
    return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}

// --- make tuple ---

template <typename T> T read(std::istream& is)
{
    T t; is >> t; cout << t << endl; return t;
}

template <typename... Args>
std::tuple<Args...> parse(std::istream& is)
{
    return std::make_tuple(read<Args>(is)...);
}

template <typename... Args>
std::tuple<Args...> parse(const std::string& str)
{
    std::istringstream ips(str);
    return parse<Args...>(ips);
};

// ---- RPC stuff

class DataSource
{
    std::string data;
public:
    DataSource(std::string s) { data = s; };
    template<class...Ts> std::tuple<Ts...> get() {  return parse<Ts...>(data);  };
};

std::map<std::string, std::function<void(DataSource*)> > functionarray;

template<typename... Args, class F>
void RegisterRPC(std::string name, F f) {
    functionarray[name] = [f](DataSource* data){apply(f, data->get<Args...>()); };
}

// --------------------- TEST ------------------

void one(int i, double d, string s)
{
    std::cout << "function one(" << i << ", " << d << ", " << s << ");\n";
}

int main()
{
    RegisterRPC<int, double, string>("test1", one);
    DataSource* data=new DataSource("5 2 huhu");
    functionarray["test1"](data);
    system("pause");
    return 0;
}

// --------------------- TEST ------------------

1 个答案:

答案 0 :(得分:2)

首先,写一个“用元组调用”。这需要一个可调用对象f和一个元组t,并调用f( std::get<0>(t), std::get<1>(t), ... )

here是堆栈溢出的许多此类实现之一。你可以在C ++ 14中编写一个更好的,或等待它到达C ++ 1z。

其次,写下data.get<std::tuple<A,B,C,...>>(),返回tuple类型A,B,C,...。这很简单:

template<class...Ts>
std::tuple<Ts...> DataSource::get() {
  return std::tuple<Ts...>{get<Ts>()...}; // some compilers get order here wrong, test!
}

现在我们的函数数组看起来像这样:

std::map<std::string , std::function<void(DataSource*)> > functionarray;

template<class...Args, class F>
std::function<void(DataSource*)> from_source( F f ) {
  // `[f = std::move(f)]` is C++14.  In C++11, just do `[f]` instead
  return [f = std::move(f)](DataSource* data){
    call_from_tuple( f, data->get<std::tuple<Args...>>() );
  };
}

template<typename... Args, class F>
void RegisterRPC( F f ) {
  functionarray.push_back( from_source<Args...>( std::move(f) ) );
}

最终用途是:

void test(int , float , char* ){ /*gets called*/ } 

void main()
{
  RegisterRPC<int,float,char*>( test )
}

我建议不要使用char*。我使用std::stringstd::vector<char>甚至std::unique_ptr<char[]>,因此生命周期非常清晰。

诀窍是我们在知道类型信息的地方擦除,这是我们包装函数的地方。在那里,我们给出了如何从数据源获取类型并调用自身的说明,留下了“数据源 - &gt;没有”类型的函数。

我们将“Ts ... - &gt; nothing”(您的F)和“(DataSource - &gt; Ts)......”(网络上的数据流)并将其组合成“DataSource - &gt;没有“(你存储的std::function