这几天看一个rpc库(https://github.com/rpclib/rpc...,server端的注册函数返回类型以及参数类型能够任意,client端只有填充对应类型的参数即可实现调用:
Server:
#include <iostream>#include "rpc/server.h"void foo() { std::cout << "foo was called!" << std::endl;}int main(int argc, char *argv[]) { // Creating a server that listens on port 8080 rpc::server srv(8080); // Binding the name "foo" to free function foo. // note: the signature is automatically captured srv.bind("foo", &foo); // Binding a lambda function to the name "add". srv.bind("add", [](int a, int b) { return a + b; }); // Run the server loop. srv.run(); return 0;}
Client:
#include <iostream>#include "rpc/client.h"int main() { // Creating a client that connects to the localhost on port 8080 rpc::client client("127.0.0.1", 8080); // Calling a function with paramters and converting the result to int auto result = client.call("add", 2, 3).as<int>(); std::cout << "The result is: " << result << std::endl; return 0;}
这在c++中想要实现是不容易的,因为c++没有运行时反射,而server显然只有在运行时能力晓得client想要执行哪个函数。并且server如何存储注册的参数类型以及返回类型各不相同的函数呢?尽管有std::function,但函数参数类型和返回类型决定了这个函数的动态类型,c++容器寄存的对象必须具备雷同类型(std::any相似机制不能解决这个问题,因为当你想要获取真正的类型时,仍然须要编译期就指定,换言之c++提供的类型变换都是在编译期指定,但rpc调用中只有在运行期能力取得类型信息,这是基本的矛盾)。
一次rpc调用须要以下步骤:
client指定函数名称,以及各个参数-----rpc库将函数名称与各个参数序列化,通过网络发送给server端-----server承受到网络数据,反序列化失去调用函数的名称和参数序列-----通过函数名称找到注册的函数对象,并传递参数序列,调用对应函数,拿到函数返回值并序列化,通过网络发送给client端-----client收到网络数据,反序列化并返回给rpcc使用者。
利用可变参数列表将client端的函数名称和参数生成std::tuple,std::tuple->二进制数据须要序列化库提供,失去序列化后的二进制数据,通过网络io发送给server,server同样通过序列化库将二进制数据反序列化为std::tuple,利用c++14的std::integer_sequence开展std::tuple作为函数参数实现调用(https://www.cnblogs.com/abeli...),利用lambda作类型擦除,记录不同的函数对象(之前看过一篇文章将c++的几种类型擦除办法,说lambda是最强的类型擦除办法,明天终于领会到什么意思了,参考:https://github.com/rpclib/rpc... 如何将不同类型的函数存储在同一动态类型中)。
上面是一些记录的尝试的代码,很乱,次要是尝试上述办法的应用:
#include <tuple>#include <iostream>#include <string>#include <utility>#include <unordered_map>class Test {public: int age; explicit Test(int a) : age(a) {} Test(const Test& t) { age = t.age; std::cout << "copy" << std::endl; } Test(Test&& t) noexcept { age = t.age; std::cout << "move" << std::endl; }};int method(Test&& a, const Test& b) { return a.age + b.age;}std::string strappend(std::string str1, std::string str2) { std::string result(str1); result.append(str2); return result;}template<typename F, typename T, size_t... l>decltype(auto) apply_impl(F& f, T&& t, std::index_sequence<l...>) { return f(std::forward<typename std::tuple_element<l, std::decay_t<T>>::type>(std::get<l>(std::forward<T>(t)))...);}template<typename F, typename T>decltype(auto) apply(F& f, T&& tuple) { return apply_impl(f, std::forward<T>(tuple), std::make_index_sequence<std::tuple_size<std::decay_t<T>>::value>());}template<typename Rt, typename... Args>struct func_traits { using ReturnType = Rt; using ArgsType = std::tuple<typename std::decay_t<Args>::type...>;};class elti {public: // just for test. template<typename T> T getElementAs(size_t i) { return T(); } template<typename TupleType> void Convert(TupleType& tuple) { ConvertInner<0>(tuple); }private: template<size_t index, typename TupleType> void ConvertInner(TupleType& tuple) { using nthType = typename std::tuple_element<index, TupleType>::type; nthType obj = getElementAs<nthType>(index); std::get<index>(tuple) = std::move(obj); ConvertInner<index + 1>(tuple); }};template<typename TupleType>class MyTupleElement { template<typename Tp> class TupleElementInner; template<typename Arg, typename... ArgsTypes> class TupleElementInner<std::tuple<Arg, ArgsTypes...>> : public TupleElementInner<std::tuple<ArgsTypes...>> { public: using BaseType = TupleElementInner<std::tuple<ArgsTypes...>>; constexpr static size_t index = BaseType::index + 1; template<typename Tp> static void PrintElement(Tp& tuple) { std::cout << std::get<index>(tuple) << std::endl; BaseType::PrintElement(tuple); } }; template<typename LastArg> class TupleElementInner<std::tuple<LastArg>> { public: constexpr static size_t index = 0; template<typename Tp> static void PrintElement(Tp& tuple) { std::cout << std::get<index>(tuple) << std::endl; } };public: static void PrintTupleElement(TupleType& tuple) { TupleElementInner<TupleType>::PrintElement(tuple); }};int main() { using namespace std::literals::string_literals; auto tu1 = std::make_tuple(Test(2), Test(3)); auto result1 = apply(method, tu1); auto tu2 = std::make_tuple(std::string("chloro"), std::string("pn")); //auto re2 = apply(strappend, tu2); //std::cout << result1 << std::endl; //std::cout << re2 << std::endl; MyTupleElement<std::tuple<std::string, std::string>>::PrintTupleElement(tu2); return 0;}
通过开展tuple包到函数调用,参考url的那个比较简单,这里实现了具备完满转发能力的形式。