• lua5.3调用C/C++


    马上面临毕业设计,打算做点跟网游有关的,先从做周边工具开始,目前正在做一个协议序列化和反序列化的东西,广告一波先: https://github.com/Anti-Magic/rproto

    目前非常简陋,功能还没做完,不要当真。。

    因为目标是绑定到lua,作为一个独立的库,不想对项目有依赖,这样的好处是客户端和服务端都可以方便的拿来用,所以打算手动绑定。

    我夜观天象发现,到目前为止网上找不到针对lua5.3的这么简明扼要且完整的示例,转载注明出处:http://www.cnblogs.com/wolfred7464/p/5147675.html

    由于lua提供的require函数可以引入C语言的动态链接库,对于使用者来说,不需配置,只要调用require就可以,肯定是最简单的方法。(其实iOS不能这样做的,唉,习惯就好)

    首先读者需要对lua提供的操作虚拟栈的函数比较熟悉,不熟悉的先翻手册吧:http://cloudwu.github.io/lua53doc/manual.html

    先上一段简单的示例,功能是使用C语言编写一个计算2个浮点数之和的add函数,绑定到lua由lua调用:

     1 #include "lua.h"
     2 #include "lualib.h"
     3 #include "lauxlib.h"
     4 #include "luaconf.h"
     5 
     6 double add(double x, double y) {
     7     return x+y;
     8 }
     9 
    10 static int ladd(lua_State* L) {
    11     double x = luaL_checknumber(L, 1);
    12     double y = luaL_checknumber(L, 2);
    13     lua_pushnumber(L, add(x+y));
    14     return 1;
    15 }
    16 
    17 int luaopen_xxx(lua_State* L) {
    18     luaL_checkversion(L);
    19 
    20     struct luaL_Reg funcs[] = {
    21         {"add", ladd},
    22         {NULL,  NULL}
    23     };
    24     luaL_newlib(L, funcs);
    25     return 1;
    26 }
    View Code

    在linux使用gcc编译:

    gcc main.c -shared -fPIC -I/usr/local/include -o xxx.so

    lua的package.cpath中需要能搜索到我们的so,设置cpath的方法网上搜一下有很多。然后lua代码中这样调用:

    1 local xxx = require "xxx"
    2 print(xxx.add(45, 5))
    View Code

    最后在终端执行:

    lua test.lua

    这个例子就跑起来了,虽然步骤说不上多简单,但是足够清晰吧。。为了简单,我就不写检查参数数量和类型是否匹配的代码了,后面的例子也是。

    如何使用lua调用C++呢?只要你会操作userdata,基本上照搬上面的C语言的方式就可以,只不过显式传递C++的this指针就可以。再上一个简单的代码:

     1 #include <lua.hpp>
     2 
     3 class TTT {
     4 public:
     5     TTT() {}
     6     ~TTT() {}
     7 
     8     double add(double x, double y) {
     9         return x+y;
    10     }
    11 };
    12 
    13 extern "C"
    14 {
    15     static int lnewTTT(lua_State* L) {
    16         TTT** p = (TTT**)lua_newuserdata(L, sizeof(TTT*));
    17         *p = new TTT();
    18         return 1;
    19     }
    20 
    21     static int ldelTTT(lua_State* L) {
    22         TTT** p = (TTT**)lua_topointer(L, 1);
    23         delete *p;
    24         *p = nullptr;
    25         return 0;
    26     }
    27 
    28     static int ladd(lua_State* L) {
    29         TTT** p = (TTT**)lua_topointer(L, 1);
    30         TTT* pt = *p;
    31         lua_pushnumber(L, pt->add(lua_tonumber(L, 2), lua_tonumber(L, 3)));
    32         return 1;
    33     }
    34 
    35     int luaopen_xxx(lua_State* L) {
    36         luaL_checkversion(L);
    37         luaL_openlibs(L);
    38 
    39         struct luaL_Reg funcs[] = {
    40             {"new", lnewTTT},
    41             {"add", ladd},
    42             {"del", ldelTTT},
    43             {NULL,  NULL}
    44         };
    45         luaL_newlib(L, funcs);
    46         return 1;
    47     }
    48 }
    View Code
    1 local xxx = require "xxx"
    2 local t = xxx.new()
    3 print(xxx.add(t, 3, 4))
    4 xxx.del(t)
    View Code

    (为什么要使用指针的指针呢?因为lua申请内存和释放内存时不会调用C++的构造函数和析构函数,我们又不能手动去调用,只能使用指针的指针,便于手动调用构造和析构函数了。)

    使用lua的userdata保存this指针,然后作为参数传递到绑定函数,然后C语言就可以调用了,第一个例子明白后,这个没什么难度。

    但是这样做有三个问题:一是无法检查lua传来的this指针是否是正确的类型,二是需要手动释放内存,三是lua代码中调用起来太麻烦。

    这三个问题有一个共同的解决方案:元表(metatable)。

    1、给同一类userdata设置同一个元表,利用元表就可以区别不同类型的userdata。

    2、lua垃圾回收时会调用元表中的"__gc"元方法,我们可以利用这个元方法析构C++对象。

    3、索引字段会调用"__index"方法,不存在这个方法时会从元表中查找,利用这个特性我们可以把red.add(t, x, y)这样的调用变成t.add(t, x, y),然后就可以写成t:add(x, y)了。

    把第二个例子中的lnewTTT和luaopen_xxx两个函数修改一下就可以,上代码:

     1 static int lnewTTT(lua_State* L) {
     2     TTT** p = (TTT**)lua_newuserdata(L, sizeof(TTT*));
     3     *p = new TTT();
     4     luaL_getmetatable(L, "xxx.TTT");
     5     lua_setmetatable(L, -2);
     6     return 1;
     7 }
     8 
     9 int luaopen_xxx(lua_State* L) {
    10     luaL_checkversion(L);
    11     luaL_openlibs(L);
    12 
    13     struct luaL_Reg funcs[] = {
    14         {"new", lnewTTT},
    15         {NULL,  NULL}
    16     };
    17 
    18     struct luaL_Reg funcs_meta[] = {
    19         {"add", ladd},
    20         {NULL,  NULL}
    21     };
    22 
    23     luaL_newmetatable(L, "xxx.TTT");
    24     lua_pushstring(L, "__gc");
    25     lua_pushcfunction(L, ldelTTT);
    26     lua_settable(L, -3);
    27     lua_pushstring(L, "__index");
    28     lua_pushvalue(L, -2);
    29     lua_settable(L, -3);
    30     luaL_setfuncs(L, funcs_meta, 0);
    31     luaL_newlib(L, funcs);
    32     return 1;
    33 }
    View Code
    1 local xxx = require "xxx"
    2 local t = xxx.new()
    3 print(t:add(3, 4))
    View Code

    基本上就是这样了,绑定其他的数据类型,或者保存上下文等,都可以查找手册解决,lua天生为了嵌入宿主语言而设计,与C语言的交互有天然的优势。

  • 相关阅读:
    CC DGCD:Dynamic GCD——题解
    BZOJ2243:[SDOI2011]染色——题解
    POJ1990:MooFest——题解
    POJ2299:Ultra-QuickSort——题解
    POJ2352:Stars——题解
    模板:树链剖分
    BZOJ1036:[ZJOI2008]树的统计——题解
    国务院正式“回应”马云:绝对不允许孩子们在30年后找不到工作!
    接口测试用例设计实践总结
    接口测试用例设计实践总结
  • 原文地址:https://www.cnblogs.com/wolfred7464/p/5147675.html
Copyright © 2020-2023  润新知