我们的想法是创建以下功能(看起来很简单)
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
}
然后,当数据来自Network时,需要将数据分解以使用适当的参数调用test。
ProcessData(data)
{
data.begin();
functionarray[data.get<char*>()] (
data.get<int>() ,
data.get<float>() ,
data.get<char*>() ); // the RegisterRPC parameters
}
我已经发现可变模板可以存储参数
可变模板的扩展形参列表
它可以将参数分解为类
如何迭代一个打包的可变参数模板列表?
所以我相信这是可能的-只是我不明白怎么…希望有人能帮忙。
编辑:如果有人对完整的解决方案感兴趣:
#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 ------------------
首先,写一个"调用元组"。它接受一个可调用对象f
和一个元组t
,并调用f( std::get<0>(t), std::get<1>(t), ... )
。
是许多这样的堆栈溢出实现之一。你可以用c++ 14写一个更好的,或者等待c++ 1z的到来。
第二,写入返回A,B,C,...
类型的tuple
的data.get<std::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::string
,或std::vector<char>
,甚至std::unique_ptr<char[]>
,所以生命周期非常清楚。
技巧是在我们知道类型信息的地方擦除,这是我们包装函数的地方。在这里,我们告诉它如何从数据源获取类型并调用它自己,留下一个类型为"data source -> nothing"的函数。
我们取"t…"(你的F)"one_answers"(数据源-> Ts)…"(通过网络的数据流)并将其组合成"DataSource -> nothing"(您存储的std::function
)。