杂记

这几天看一个rpc库(https://github.com/rpclib/rpc...,server端的注册函数返回类型以及参数类型可以任意,client端只要填充对应类型的参数即可完成调用:
Server:

#include 
#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 
#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();
    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 

#include 

#include 

#include 

#include 

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

decltype(auto) apply_impl(F& f, T&& t, std::index_sequence) {

 return f(std::forward>::type>(std::get(std::forward(t)))...);

}

template

decltype(auto) apply(F& f, T&& tuple) {

 return apply_impl(f, std::forward(tuple), std::make_index_sequence>::value>());

}

template

struct func_traits {

 using ReturnType = Rt;

 using ArgsType = std::tuple::type...>;

};

class elti {

public:

 // just for test.

 template

 T getElementAs(size_t i) {

 return T();

 }

 template

 void Convert(TupleType& tuple) {

 ConvertInner<0>(tuple);

 }

private:

 template

 void ConvertInner(TupleType& tuple) {

 using nthType = typename std::tuple_element::type;

 nthType obj = getElementAs(index);

 std::get(tuple) = std::move(obj);

 ConvertInner(tuple);

 }

};

template

class MyTupleElement {

 template

 class TupleElementInner;

 template

 class TupleElementInner> : public TupleElementInner> {

 public:

 using BaseType = TupleElementInner>;

 constexpr static size_t index = BaseType::index + 1;

 template

 static void PrintElement(Tp& tuple) {

 std::cout << std::get(tuple) << std::endl;

 BaseType::PrintElement(tuple);

 }

 };

 template

 class TupleElementInner> {

 public:

 constexpr static size_t index = 0;

 template

 static void PrintElement(Tp& tuple) {

 std::cout << std::get(tuple) << std::endl;

 }

 };

public:

 static void PrintTupleElement(TupleType& tuple) {

 TupleElementInner::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>::PrintTupleElement(tu2);

 return 0;

}

通过展开tuple包到函数调用,参考url的那个比较简单,这里实现了具有完美转发能力的方式。

你可能感兴趣的