耍一把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);
}

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

在函数的接口上,基本上就是识别到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);
    }
}

函数参数是依循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

脚本识别到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());
    }

};

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

时间: 2024-08-12 06:17:31

耍一把codegen,这样算懂编译么?的相关文章

android反编译odex文件

关于android的反编译工具,相信大家并不陌生 如APK-TOOL,dex2jar APK-TOOL 用于反编译出布局文件 下载地址http://code.google.com/p/android-apktool/downloads/list dex2jar 用于将dex反编译成.jar包 下载地址:http://code.google.com/p/dex2jar/downloads/list 生成的jar包可用jd-gui来查看(前提是代码未被混淆) 但是如果我们从rom里面提取出的apk是

在linux系统中编译C

那么这里给大家说的是shell的一些编译指令,其实和DOS差不多,就是指令不同(注意哦,linux是没有磁盘概念的,不要还傻乎乎的在那儿输入c盘d盘,嘻嘻) 哎!一周的作业没做,完了,写完这篇blog赶快撸作业,5555好惨. 啊啊啊!还有推荐大家钱婆的歌,ke¥ha,可以搜他的crazyboy,so  good! 来来!那么我是用的树莓派的linux系统,但大家需要知道安卓也用到了他,具体俺也不知道,管他呢,知道就行,right? ok,那么首先是vi进入编译模式,就像打开了vc一样,(不过好

JAVA虚拟机学习笔记(一)Windows10下编译OpenJDK8

转载请注明源地址:http://www.cnblogs.com/lighten/p/5906359.html 1. 编译环境的准备 1.1 JDK源码下载 OpenJDK是JAVA发展史中的一个开源项目,本文以OpenJDK8为例进行编译.OpenJDK的官网为:http://openjdk.java.net/,直接访问http://openjdk.java.net/install/index.html进入主要界面.左侧有一系列的,找到Source code列表,其提供了两种形式的下载:Merc

Fedora20 编译安装qemu-system

安装简介: 1.1. 本次编译安装所有的操作都在Fedora 20 x86-64上,内核版本为: 3.14.4-200.fc20.x86_64.如果在其他系统编译安装,请看其他文章. 2.安装准备: 2.1 安装依赖和编译环境../configure一些必要的依赖不足不会报错,所以下面我安装的依赖并不是完全安装了依赖,但是至少能编译通过.可能安装很多没用的包,谁叫我不 懂编译. yum install automake autoconfig clang gcc gcc-c++ cdk-devel

idea13编译后没有配置文件的问题

新装了idea13,想赶快耍起,却没有想到编译的时候总是没有配置文件.我试了不少方法,比如将配置文件所在的目录设为"source",不行再设为"resource",重设导出目录,但是都没有效果. 后来看到一篇文章说提到了idea的compiler中的文件类型配置,我检查了一下,终于确实是compiler的配置导致的.在新版本的idea配置中,没有将properties和xml类型的文件默认为可编译的文件.只需要在Setting->Compiler->Re

不要困在自己建造的盒子里——写给.NET程序员(附精彩评论)

转自:http://kb.cnblogs.com/page/92260/ 此文章的主旨是希望过于专注.NET程序员在做好工作.写好.NET程序的同时,能分拨出一点时间接触一下.NET之外的东西(例如10%-20%的时间),而不是鼓动大家什么都去学最后什么都学不精,更不是说.NET不行或劝大家放弃.NET.恕我愚钝,此主旨在文中表达不够清楚,看评论中很多朋友误解了,特此说明. 另外,本文中的观点并不全部是我个人的想法,相当一部分来自我以前聊过天的某些大牛,他们很多来自微软.百度.腾讯等知名企业,并

还在吐槽翻译的外版书质量差吗?谈谈我个人的理解

很难想象哪个学习计算机技术的人是没看过这方面书籍的,如果只是在网上看看技术贴,那样得来的知识绝对是离散的,不系统的.而要真正学好一门学问(比如一门计算机语言或者一门技术),一本好书的作用是不言而喻的.很多人抱怨国人在技术图书方面抄来抄去,不求甚解,虽然出版图书者甚众,但最终成为精品者却凤毛麟角.于是,更多读者热衷于外版书.但显然,并非所有国人的外语水平都足以在阅读原版书籍时毫无障碍.那么退而求其次,寻求翻译版就成为一种看似不得已的选择. 不幸的是,网上对于翻译版书籍的吐槽可以说从未消停.我也看过

Python伪开发者对于搜狐云景的测评

Python伪开发者对于搜狐云景的测评 本人是GAE和OpenShift的狂热爱好者,玩过各种国外PaaS.某次想搞个稍微复杂点的Python Web程序,需要比较好的网络传输速度,就试图找前PM(Project Manager)要个国内的VPS耍一把.前PM表示近来搞了个搜狐云景的公测激活码,让我先试试,于是就有了我在SCE的第一个奇怪的Python应用. PS: SCE是搜狐云景是搜狐公司自主研发的与语言无关.可提供弹性伸缩服务的公有云PaaS平台,现致力发展成为最开放的PaaS平台. (无

玩转ptrace (一)

转自http://www.cnblogs.com/catch/p/3476280.html [本文翻译自这里: http://www.linuxjournal.com/article/6100?page=0,0,作者:Pradeep Padaia] 你是否曾经想过怎样才能拦截系统调用?你是否曾经想过通过修改一下系统调用的参数来耍一把内核?你是否想过调试器是怎样把一个进程停下来,然后把控制权转移给你的?如果你以为这些都是通过复杂的内核编程来实现的,那你就错了,事实上,Linux 提供了一种很优雅的