首页 > 代码库 > 耍一把codegen,这样算懂编译么?

耍一把codegen,这样算懂编译么?

最近使用protobuf搭了些服务器,对protobuf的机制略感兴趣,所以研究了下。

大致分析没有什么复杂的

1 对定义的结构体生成消息封包协议

2 对定义的rpc函数生成接口定义

3 用户按protobuf的接口定义实现对应的调用接口

实现上,也颇简单比如如下的一个protobuf文件

// ConnectServerRequest和ConnectServerReply是客户端和服务端建立连接后的第一个RPC请求// 该请求不包括认证过程,认证过程由Entity去处理,这个只是建立连接,从而启动Entity通信流程message ConnectServerRequest {    enum RequestType {        NEW_CONNECTION = 0;    // 新登录        RE_CONNECTION = 1;    // 断线快速重连        BIND_AVATAR = 2;    // 重新绑定entity到avatar    }    optional bytes  routes = 1;         required RequestType type = 2;  // 认证类型    optional bytes  deviceid = 3;  // 设备 id, 标示客户端,可用mac地址    optional bytes  entityid = 4;     // 断线重连或者BIND_AVATAR的时候需要的avatar entity id    optional bytes  authmsg = 5;     // 验证消息}// 客户端发给Gate服务器service IGateService {    // 连接服务器,进行认证    rpc connect_server(ConnectServerRequest) returns (Void);}
View Code

要生成对应的接口文件,消息协议不提,大概就是按一定的顺序在内存中组织下变量的布局,稍复杂的大概就是考虑下大端小端的问题。

在函数的接口上,基本上就是识别到rpc这个关键字,然后提取出函数定义的关键语义比如函数名,参数(类型及实参名),返回值类型。这个过程大概算词法分析?编译上的术语大致如此。

protobuf生成的代码大致如下:

virtual void connect_server(::google::protobuf::RpcController* controller,                       const ::mobile::server::ConnectServerRequest* request,                       ::mobile::server::Void* response,                       ::google::protobuf::Closure* done);void IGateService_Stub::connect_server(::google::protobuf::RpcController* controller,                              const ::mobile::server::ConnectServerRequest* request,                              ::mobile::server::Void* response,                              ::google::protobuf::Closure* done) {  channel_->CallMethod(descriptor()->method(2),                       controller, request, response, done);}void IGateService::CallMethod(const ::google::protobuf::MethodDescriptor* method,                             ::google::protobuf::RpcController* controller,                             const ::google::protobuf::Message* request,                             ::google::protobuf::Message* response,                             ::google::protobuf::Closure* done) {  GOOGLE_DCHECK_EQ(method->service(), IGateService_descriptor_);  switch(method->index()) {    case 0:        connect_server(controller,             ::google::protobuf::down_cast<const ::mobile::server::ConnectServerRequest*>(request),             ::google::protobuf::down_cast< ::mobile::server::Void*>(response),             done);    }}
View Code

函数参数是依循protobuf的消息协议生成的结构体,大致上是类似json的k-v结构。

原理分析完,我大概自己写了一个,学protobuf定义了一个rpccall,然后没有定义过于复杂的结构,直接作为一个c++的关键字使用,编译前用我自己写的脚本随便处理下

c++源代码如下:

/* * acceptservice.h * *  Created on: 2014-11-3 *      Author: qianqians */#ifndef _acceptservice_h#define _acceptservice_h#include "service.h"namespace Fossilizid{namespace reduce_rpc{RPCCALL std::string init();class acceptservice : public service{public:    acceptservice(char * ip, short port);    ~acceptservice();private:    RPCCALL std::tuple<int, std::string, float> run_network(int count);    RPCCALL std::pair<int, int> run_network(int count, int count1);private:    remote_queue::ENDPOINT ep;    remote_queue::QUEUE que;    remote_queue::ACCEPTOR acp;};} /* namespace reduce_rpc */} /* namespace Fossilizid */#endif //_acceptservice_h
View Code

脚本识别到rpccall关键字之后,则提取对应的词法树到一个json串

{‘acceptservice.h‘: {‘templateclassfunc‘: {}, ‘classfunc‘: {‘acceptservice‘: [[‘std::tuple<int, std::string, float>‘, ‘run_network‘, ‘int count‘], [‘std::pair<int, int>‘, ‘run_network‘, ‘int count‘, ‘int count1‘]]}, ‘globalfunc‘: [[‘std::string‘, ‘init‘]], ‘templateglobalfunc‘: []}, ‘acceptservice.cpp‘: {‘templateclassfunc‘: {}, ‘classfunc‘: {}, ‘globalfunc‘: [], ‘templateglobalfunc‘: []}}

然后生成对应的客户端代码:

大致就是生成对传入参数的json格式打包以及发送,和等待服务器的响应返回

生成代码如下:

#include <IRemoteEndpoint.h>std::string init(IRemoteEndpoint ep){    boost::shared_ptr<session> s = GetSession(ep);    Json::Value value;    value[epuuid] = s.enppui();    value[suuid] = UUID();    value[eventtype] = rpc_event;    value[rpc_event_type] = call_rpc_mothed;    value[fnargv] = Json::Value(Json::objectValue) ;    value[fnname] = init;    s->do_push(s, value);    Json::Value ret = _service_handle->wait(value[suuid].asString(), 1);    if (ret[suuid] != value[suuid]){        throw std::exception("error suuid")    }    return  ret[rpcret].asString();}class acceptservice{private:    IRemoteEndpoint ep;    acceptservice(IRemoteEndpoint _ep){        ep = _ep;    }public:    std::tuple<int, std::string, float> run_network(int count){        boost::shared_ptr<session> s = GetSession(ep);        Json::Value value;        value[epuuid] = s.enppui();        value[suuid] = UUID();        value[eventtype] = rpc_event;        value[rpc_event_type] = call_rpc_mothed;        value[fnargv] = Json::Value(Json::objectValue) ;        value[fnargv][count] = count;        value[fnname] = run_network_int;        s->do_push(s, value);        Json::Value ret = _service_handle->wait(value[suuid].asString(), 1);        if (ret[suuid] != value[suuid]){            throw std::exception("error suuid")        }        return std::make_tuple(ret[rpcret][0].asInt(), ret[rpcret][1].asString(), ret[rpcret][2].asFloat());    }    std::pair<int, int> run_network(int count, int count1){        boost::shared_ptr<session> s = GetSession(ep);        Json::Value value;        value[epuuid] = s.enppui();        value[suuid] = UUID();        value[eventtype] = rpc_event;        value[rpc_event_type] = call_rpc_mothed;        value[fnargv] = Json::Value(Json::objectValue) ;        value[fnargv][count] = count;        value[fnargv][count1] = count1;        value[fnname] = run_network_int_int;        s->do_push(s, value);        Json::Value ret = _service_handle->wait(value[suuid].asString(), 1);        if (ret[suuid] != value[suuid]){            throw std::exception("error suuid")        }        return std::make_pair(ret[rpcret][ret0].asInt(), ret[rpcret][ret1].asInt());    }};
View Code

看起来,还算好看,以上!

 

耍一把codegen,这样算懂编译么?