zoukankan      html  css  js  c++  java
  • 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语言的交互有天然的优势。

  • 相关阅读:
    evernote100个做笔记的好方法
    平衡二叉树的调整模版
    晨间日记的奇迹
    hdu 2952 Counting Sheep
    hdu 1535 Invitation Cards
    poj 3259 Wormholes(spfa)
    poj 2263 Heavy Cargo(floyd)
    poj 3268 Silver Cow Party(SPFA)
    hdu 1690 Bus System
    hdu 3631 Shortest Path(Floyd)
  • 原文地址:https://www.cnblogs.com/wolfred7464/p/5147675.html
Copyright © 2011-2022 走看看