基本思路
基本思想是您希望将函数封装在一些包装对象中,该对象将处理一些通用输入/输出并将它们映射到您的底层函数所期望的内容。
首先让我们创建一个用于存储任何值的类型:
// Dummy implementation which only works for some type.
class Value {
long value_;
public:
template<class T>
T get()
{
return (T) value_;
}
template<class T>
Value& operator=(T const& x)
{
value_ = x;
return *this;
}
};
让我们使用泛型参数隐藏我们的函数:
typedef std::function<Value(std::vector<Value>&)> Function;
我们现在想要包装任何函数指针,以符合这个签名。包装函数应该解开参数,调用真正的函数并将结果包装在一个值中:
template<class F> class FunctionImpl;
template<class R, class... T>
class FunctionImpl<R(*)(T...)>
{
R(*ptr)(T... args);
template<std::size_t... I>
Value call(std::vector<Value>& args, integer_sequence<std::size_t, I...>)
{
Value value;
value = ptr(args[I].get< typename std::tuple_element<I, std::tuple<T...>>::type >()...);
return value;
}
public:
FunctionImpl(R(*ptr)(T... args)) : ptr(ptr) {}
Value operator()(std::vector<Value>& args)
{
constexpr std::size_t count = std::tuple_size<std::tuple<T...>>::value;
if (args.size() != count)
throw std::runtime_error("Bad number of arguments");
return call(args, make_integer_sequence<std::size_t, std::tuple_size<std::tuple<T...>>::value>());
}
};
integer_sequence 和 make_integer_sequence 是标准 C++17 库的一部分,但您可以编写自己的实现。
我们现在定义一个注册可调用函数的类型:
class Functions {
private:
std::unordered_map<std::string, Function> functions_;
public:
template<class F>
void add(std::string const& name, F f)
{
functions_[name] = FunctionImpl<F>(std::move(f));
}
Value call(std::string name, std::vector<Value>& args)
{
return functions_[name](args);
}
};
我们可以使用它:
int foo(int x, int y)
{
std::printf("%i %i\n", x, y);
return x + y;
}
int main()
{
Functions functions;
functions.add("foo", &foo);
std::pair<std::string, std::vector<Value>> request = parse_request();
Value value = functions.call(request.first, request.second);
generate_answer(value);
return 0;
}
具有虚拟 RPC 通信功能:
std::pair<std::string, std::vector<Value>> parse_request()
{
std::vector<Value> args(2);
args[1] = 8;
args[0] = 9;
return std::make_pair("foo", std::move(args));
}
void generate_answer(Value& value)
{
std::printf("%i\n", value.get<int>());
}
我们得到:
8 9
17
当然,这是高度简化的,如果你想概括它,你会面临很多问题:
序列化
处理序列化的方法是使用通用编程进行序列化/反序列化:
template<class T> class Type {};
typedef std::vector<char> Buffer;
// I'm clearly not claiming this would be efficient, but it gives
// the idea. In pratice, you might want to consume some streaming I/O
// API.
class Value {
Buffer buffer_;
public:
template<class T>
T get()
{
return deserialize(Type<T>(), buffer_);
}
template<class T>
Value& operator=(T const& x)
{
serialize(x, buffer_);
return *this;
}
};
inline std::uint32_t deserialize(Type<std::uint32_t>, Buffer const& buffer)
{
if (buffer.size() != sizeof(std::uint32_t))
throw std::runtime_error("Could not deserialize uint32");
std::uint32_t res;
memcpy(&res, buffer.data(), sizeof(std::uint32_t));
return be32toh(res);
}
inline void serialize(std::uint32_t value, Buffer const& buffer)
{
buffer.resize(sizeof(std::uint32_t));
value = htobe32(value);
memcpy(buffer.data(), &value, sizeof(std::uint32_t));
}
另一种可能性是使用泛型编程,让Function 进行序列化/反序列化。