博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我是如何写出1W行C++代码的
阅读量:6142 次
发布时间:2019-06-21

本文共 7269 字,大约阅读时间需要 24 分钟。

标题党,真正题目应该是我是如何生成出1W行C++代码的。

最近使用swoole开发一个斗地主服务端的代理层,任务不难,排除几个swoole的 segment fault(注1) 都好说。通俗点说就是将socket转变成websocket。这个很简单,关键的是也不知道哪个混蛋在最初的时候不使用浏览器的 typed array 去解析协议而是想到了将协议 struct 转变成 json 给客户端读(注2) 。这个就蛋疼了。

浩大的工程量开始了:

每一个文件都是一条协议

我的天哪

当然幸好95%都不是我写的。不过剩下的5%也不是人能承受的。

虽然我写了一个PHP的c struct 分析器使得以下这样变为了可能。

$struct = <<
encode(false)->unpack($body);

但是这只是很简单的分析,对于变态的 C++ 就显得很无能了。

//在使用中的(时效)道具struct RespUsingPropList{    enum { XY_ID = CMDT_RESPUSINGPROPLIST };        int askid;    int num;    int proptype[MAX_PROP_NUM];//时效类type    int timeEnd[MAX_PROP_NUM];        void reset() { memset(this, 0, sizeof(*this)); }    RespUsingPropList() { reset(); }    friend bostream& operator<<(bostream& bos,const RespUsingPropList& rhs)    {        bos << rhs.askid;        bos << rhs.num;        for(int i=0;i
>(bistream& bis,RespUsingPropList& rhs) { rhs.reset(); bis >> rhs.askid; bis >> rhs.num; for(int i=0;i
> rhs.proptype[i]; bis >> rhs.timeEnd[i]; } return bis; } };

对的,他使用的不是 memcpy,使用的是运算符的重载。struct只是控制了溢出跟顺序,里面的内容它并不控制了。

这让我非常的愤慨,既然这样我只能拿出大杀器了。

装逼

具体的装逼思路是这样的:找一个 C++ 语法分析器,解析出AST,遍历一下生成C++的代码(因为协议文件是C++的,为了能利用只好是C++的了),然后再包装成PHP扩展,最后给PHP调用。

我操,这么崎岖的装逼路线已经超越了我的能力范畴了。

不过幸好在装逼路上我找到了 、 再加上一个Antlr 3的PHP runtime,我操完美啊。

当然这条路还是非常崎岖的,毕竟我在最开始想的太美好了。比如至今没找到能生成C++ PHP Parser的Antlr 语法描述文件。找到都是Java C++的。尝试的改了一下发现。 No Zuo No Die啊。

struct定义的语法规则

后来发现不行啊,卡在AST这条路上太久了(虽然可以使用其他工具生成AST.xml然后PHP分析),便果断退而求其次,来来Parser没有,Lexer总有吧,找到一个C的Antlr语法描述文件。点击生成Generate Lexer Code居然真的生成了。然后咱们就用起来呗。

使用CLexer

剩下的都是好写的。

static Php::Value pack
(Php::Parameters &params) { try { Php::Value tmp; Php::Value arr = params[0];
obj; obj.reset();
obj.
= (int)arr.get("
");
obj.
= (int)arr.get("
");

最终生成的代码是这样的,非常简单是不是啊,毕竟引入了原来的Struct文件跟PHP-CPP封装了好多东西。

static Php::Value packPlayerConnect(Php::Parameters &params)    {        try {            Php::Value tmp;            Php::Value arr = params[0];            Protocol::V10::ToolMobile::PlayerConnect obj;            obj.reset();                    obj.askid = (int)arr.get("askid");                        tmp = arr.get("userid");            if(!tmp.isString()) {                throw Php::Exception("userid is not a string");            } else {                memcpy(obj.userid, (const char *)tmp, tmp.length() >= (Protocol::V10::ToolMobile::MAX_USERID+1) ? (Protocol::V10::ToolMobile::MAX_USERID+1) : tmp.length());            }                        obj.numid = (int)arr.get("numid");                                    tmp = arr.get("sessionid");            if(!tmp.isString()) {                throw Php::Exception("sessionid is not a string");            } else {                memcpy(obj.sessionid, (const char *)tmp, tmp.length() >= (16) ? (16) : tmp.length());            }                        obj.logintype = (int)arr.get("logintype");                        obj.gameid = (int)arr.get("gameid");                        tmp = arr.get("passwd");            if(!tmp.isString()) {                throw Php::Exception("passwd is not a string");            } else {                memcpy(obj.passwd, (const char *)tmp, tmp.length() >= (Protocol::V10::ToolMobile::MAX_PWD+1) ? (Protocol::V10::ToolMobile::MAX_PWD+1) : tmp.length());            }                        tmp = arr.get("devid");            if(!tmp.isString()) {                throw Php::Exception("devid is not a string");            } else {                memcpy(obj.devid, (const char *)tmp, tmp.length() >= (Protocol::V10::ToolMobile::MAX_DEVID+1) ? (Protocol::V10::ToolMobile::MAX_DEVID+1) : tmp.length());            }                        tmp = arr.get("nickname");            if(!tmp.isString()) {                throw Php::Exception("nickname is not a string");            } else {                memcpy(obj.nickname, (const char *)tmp, tmp.length() >= (Protocol::V10::ToolMobile::MAX_NICKNAME+1) ? (Protocol::V10::ToolMobile::MAX_NICKNAME+1) : tmp.length());            }                        obj.clienttype = (int)arr.get("clienttype");                        obj.osver = (int)arr.get("osver");                        obj.ip = (int)arr.get("ip");                        obj.channelid = (int)arr.get("channelid");                        obj.version = (int)arr.get("version");                        obj.devtype = (unsigned char)(int)arr.get("devtype");                        obj.areaid = (int)arr.get("areaid");                        tmp = arr.get("token");            if(!tmp.isString()) {                throw Php::Exception("token is not a string");            } else {                memcpy(obj.token, (const char *)tmp, tmp.length() >= (Protocol::V10::ToolMobile::MAX_TOKEN+1) ? (Protocol::V10::ToolMobile::MAX_TOKEN+1) : tmp.length());            }                        obj.loginflag = (int)arr.get("loginflag");                    char buffer[Protocol::PROTOCOL_MAXSIZE];            bostream bos;            bos.attach(buffer, sizeof(obj));            bos << obj;                    Php::Value str(buffer, (int)bos.length());            return str;        } catch(biosexception e) {            char error[32];            sprintf(error, "exception: %d", e.m_cause);            throw Php::Exception(error);        }    }

然后开心的执行一下:

make clean && make  && sudo mv ddz_protocol.so /usr/lib/php5/20131226/

不对再返回回去修修改改,将他放到正式环境。

$data = \DDZProtocol::packPlayerConnect([            'askid' => 0,            'userid' => $userid,            'numid' => 0,            'sessionid' => '',            'logintype' => $logintype,            'gameid' => $this->gameId,            'passwd' => $passwd,            'devid' => '',            'nickname' => '',            'clienttype' => 2,            'osver' => 10000,            'ip' => $ip,            'channelid' => 10001,            'version' => 10104,            'devtype' => 0,            'areaid' => 0,            'token' => $token        ]);        var_dump(base64_encode($data));//        $data  = pack('i', 0);// 1. askid//        $data .= $this->packStr($userid);// 2. userid//        $data .= pack('i', 0);// 3. numid//        $data .= $this->packStr('');// 4. sessionid//        $data .= pack('i', $logintype);// 5. logintype//        $data .= pack('i', $this->gameId);// 6. gameid//        $data .= $this->packStr($passwd);// 7. passwd//        $data .= $this->packStr('');// 8. devid//        $data .= $this->packStr('');// 9. nickname//        $data .= pack('i', 2);// 10. clienttype//        $data .= pack('i', 10000);// 11. osver 操作系统版本号//        $data .= pack('i', (int)$ip);// 12. ip//        $data .= pack('i', 10001);// 13. channelid//        $data .= pack('i', 10104);// 14. version//        $data .= pack('C', 0);// 15. devtype//        $data .= pack('i', 0); // 16. areaid//        $data .= $this->packStr($token);// 17. token

玩一下斗地主,居然成功了,顿时觉得世界非常的美好。如果我将全部代码生成我操,那将是我第一个1W行代码的C++文件。哇哈哈哈哈哈哈。

总结:合理利用工具,你将在装逼的路上越走越远。

顺便无耻的回答了下 无耻的人的问题:

注1:

确实是swoole的问题,因为将代码写法从

var func = function(){    blabla...    setTimeout(func, 2000);};

改成

var func = function(){    setInterval(function(){        blabla...    }, 2000);};

都能提升服务稳定性。

注2:

额 当然也是有好处的 gbk转换成unicode 对于前端来说还是需要码表的 这个放在移动端就不好了...
还有客服端跟服务端做了AES加密....

转载地址:http://kpjya.baihongyu.com/

你可能感兴趣的文章
简洁优雅地实现夜间模式
查看>>
react学习总结
查看>>
微软正式发布PowerShell Core 6.0
查看>>
Amazon发布新的会话管理器
查看>>
InfoQ趋势报告:DevOps 和云计算
查看>>
舍弃Python,为什么知乎选用Go重构推荐系统?
查看>>
在soapui上踩过的坑
查看>>
MySQL的字符集和字符编码笔记
查看>>
ntpd同步时间
查看>>
must implement java.io.Serializable hessian
查看>>
Microsoft Licenses Flash Lite for Windows Mobile Users
查看>>
HDOJ 2020 绝对值排序
查看>>
HDOJ/HDU 2560 Buildings(嗯~水题)
查看>>
Maven编译时跳过Test
查看>>
Spring Boot 整合Spring Security 和Swagger2 遇到的问题小结
查看>>
[20170628]12C ORA-54032.txt
查看>>
除以2
查看>>
高可用集群原理解析
查看>>
Nginx配置URL转向tomcat
查看>>
极客Web前端开发资源大荟萃#001
查看>>