zoukankan      html  css  js  c++  java
  • 耍一把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

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

  • 相关阅读:
    NSURLSession学习笔记(二)Session Task
    NSURLSession学习笔记(一)简介
    iOS Core Animation 简明系列教程
    iOS夯实:RunLoop
    mysql主从复制读写分离
    git文件名大小写问题
    mysql zip 安装
    maven dependencies与dependencyManagement的区别
    Eclipse使用技巧
    相关开发软件http代理设置 windows
  • 原文地址:https://www.cnblogs.com/qianqians/p/4168332.html
Copyright © 2011-2022 走看看