引言

通过前几篇,我们已经对Lua的C API有了一定的了解,如lua_push*、lua_is*、lua_to*等等。用C++调用Lua数据时,我们主要运用lua_getglobal与lua_push*配合以达到目的。现在我们来试试用Lua调用C++数据。

C++数据类型映射到Lua

C++中数据类型有这么几种:1、内建的int、float等;2、指针,如void *、int *、int (*fun)(int, int)等;3、用户自定义的class、strcut等。Lua中C API支持操作的数据类型有如下:

见名知意,从他们的参数,我们就可以看出来他们的作用。比如:lua_pushlightuserdata用来将指针压栈,lua_pushcclosure用来将函数压栈,不一而足。通过这些API,我们可以将C++中的数据结构一一映射到Lua中。

Lua调用C++内置常用数据类型与函数

我们要将一个值,从C++传入Lua,必须有两个步骤:1、值是多少?通过lua_push*将值压入栈顶,此时该值的类型与值的大小已确定;2、用什么名字来引用该值?通过lua_setglobal来用一个名字引用栈顶的值。我们来按照这个步骤,尝试一下将一个变量传入Lua,代码如下:

#include <iostream>

#include <fstream>

#include <string>

using namespace std;

 

extern "C"

{

 

#include <lua.h>

#include <lauxlib.h>

#include <lualib.h>

};

 

void TestLua2();

int main()

{

    TestLua2();

    return 0;

}

 

void TestLua2()

{

    lua_State *L = luaL_newstate();

    luaopen_base(L); //

    luaopen_table(L); //

    luaopen_package(L); //

    luaopen_io(L); //

    luaopen_string(L); //

    luaL_openlibs(L); //打开以上所有的lib

    

    int valueCPP = 1;

 

    // 将a值压入栈顶

    lua_pushnumber(L, valueCPP);

    // 命名栈顶的值

    lua_setglobal(L, "valueCPP");

 

    string str;

    while (true)

    {

        cout << "输入lua文件路径:" << endl;

        getline(cin, str, '\n');

        if (luaL_loadfile(L, str.c_str())

            || lua_pcall(L, 0, 0, 0) )

        {

            const char * error = lua_tostring(L, -1) ;

            cout << string(error) << endl;

        }

    }

 

}

Lua文件中代码如下:

运行结果如下:

将函数传入Lua有点复杂,如何确定函数的参数?如何确定函数的返回值及其个数?我们一起去查阅Lua文档,看看文档怎么说。

 

翻译如下:将一个C函数入栈。这个函数接受一个C函数指针,将其压入作为一个Lua的function类型值压入栈,当这个值被调用时,Lua将调用该值对应的C函数。所有注册在Lua中的C函数必须遵守一个正确的协议来接受参数和返回返回值(查看lua_CFunction)。

翻译如下:为了正确的与Lua交互,必须遵守如下协议,该协议定义了参数和返回值的传递方式:一个C函数从Lua的栈中以直接(左边的参数先入栈)顺序来接收参数。因此,当该函数的调用开始时,lua_gettop(L) 得到用来调用该函数的参数个数(注:lua_gettop的作用是得到栈的栈顶元素的索引,即栈的长度)。第一个参数(如果有的话)的值索引为1,最后一个参数的值索引为lua_gettop(L)。为了将返回值返回给Lua,C函数将所有的返回值以直接的顺序(第一个返回值先入栈,以此类推)全部压入栈中,然后将返回值的个数作为值返回(注:此处的返回是return之意,不是传递到Lua中)。所有栈中其他的在返回值个数之下的索引对应的值会被Lua忽略掉,被Lua调用的C函数能传递多个值返回给Lua。例子我就不翻译了。

按照其描述,我们先来试试无参无返回值的函数:

lua文件内容如下:

结果如下:

现在,我们定义一个函数,接收两个int类型的参数,返回两个值:和与差。代码如下:

Lua文件内容如下:

结果如下:

以上,即可完成从Lua中调用C++函数。这里有个技巧,我们可以在Lua中改变在C++里定义的函数名,即调用lua_setglobal时指定该函数的名字。如果不想改变,我们可以定义一个宏来省略掉这些代码,如下:

这样,即可用一行代码,将函数在Lua中声明。

Lua调用C++中自定义类型

我低估了这么做的复杂度,这个内容我想准备一整个篇幅来描述。请静待下一篇。(请点击这里

将C++里的数据打包成dll供Lua调用

前面的操作,本质上还是Lua寄宿在C++中的。现在,我们将C++寄宿在Lua中。我们先回顾一下luaopen_*这一系列的API:

这些API的作用是打开相应的库,比如luaopen_string,打开lua的字符串操作库。如果不打开这个库,那么我们就无法使用string.format之类的操作,因为这些操作的定义是在这些库中实现的。我们先来看一下string库这个文件,它位于src目录下lstrlib.c。详细内容我就不贴出来了,主要看看最下面的代码:

如果我们要为Lua写扩展模块,我们必须遵循下面几条规定:1、必须有一个luaL_Reg结构,以便将库中所有的函数映射到Lua中,如上图。2、必须定义一个luaopen_*函数并且导出它,*为dll的名字(注意,函数的luaopen_后面的字符必须与dll的命名一致)。3、映射入Lua的函数,其参数的传递,必须按照Lua的规定(就是上文面描述的)。我们先来试试,首先修改CMakeLists.txt,新添加一个项目,用来构建Dll:

project(LuaTest)

 

include_directories(AFTER ${CMAKE_SOURCE_DIR})

 

##########lua静态库

set(LIB_FILES lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c lbitlib.c lcorolib.c ldblib.c liolib.c lmathlib.c loslib.c lstrlib.c ltablib.c loadlib.c linit.c)

source_group("\\libFiles" FILES ${LIB_FILES})

add_library (LuaLib STATIC ${LIB_FILES})

 

 

 

###########c++与lua交互###################

add_executable(LuaWithCPPTest source.cpp)

###########source_group("\\headFiles" FILES source.cpp)

target_link_libraries(LuaWithCPPTest LuaLib)

 

 

############lua解释器###########

add_executable(LuaInterpreter lua.c ${LIB_FILES})

 

############DLL#################

add_library(LuaDll SHARED sourceDll.cpp)

target_link_libraries(LuaDll LuaLib )

 

 

#ADD_DEFINITIONS(-DLUA_LIB -DLUA_BUILD_AS_DLL)

######################################define LUA_LIB##################################################

######################################define LUA_BUILD_AS_DLL#########################################

其中的DLL即是我们新添加的项目,sourceDll.cpp文件内容如下:

 

extern "C"

{

 

#include "lua.h"

#include "lauxlib.h"

#include "lualib.h"

};

 

 

static int Test(lua_State *L)

{

    lua_pushnumber(L, 1);

    printf("This is a message from c++ dll\n");

    return 0;

}

 

static const struct luaL_Reg mydlllib[] = {

    {"Test", Test},

    {NULL,NULL},

};

 

extern "C" int __declspec(dllexport)luaopen_LuaDll(lua_State *L)

{

    printf("luaopen_LuaDll invoked\n");

    luaL_newlib(L, mydlllib); // 5.2之前使用luaL_register(L, "modulename", modulename);

 

    return 1;

}

得到的项目结构如下:

构建好LuaDll后,我们可以在其目录下,运行LuaInterpreter,在解释器中require,具体如下:

运行,得到结果如下:

我们观察它的输出,第一句是:“luaopen_LuaDll invoked”,这句话表明程序确实走到了我们构建的DLL中,接着输出“multiple Lua VMs detected”,这句话翻译过来就是:检测到多个Lua虚拟机。这就是我前面说的那个坑了!!!网上搜资料,然后自己摸索,总结出的经验如下:自定义扩展Lua的DLL时,要求其DLL构建时,引用的是Lua动态库,而不是静态库(在这里,我们现在引用的是静态库)。如果我们自己的宿主也要支持扩展DLL的话,宿主也必须是引用Lua的动态库。生成Lua动态库时要定义宏LUA_LIB和LUA_BUILD_AS_DLL,以便导出Lua函数符号,如果没定义这两个宏,那么生成DLL时,将没有.lib文件生成。为什么是这两个宏呢?请以这两个宏为关键字搜索src目录下luaconf.h文件,即可找到答案。既然知道原因了,我们来修改CMakeLists.txt,使之生成动态库,并且在其中定义这两个宏,结构如下:

重新生成即可,然后在我们的项目中导入,具体步骤及效果如下图:

 

tst.lua内容如下:

至此,我们便可以通过DLL的形式扩展Lua了。

总结

通过DLL,我们可以很方便的扩展Lua,从Lua中调用C++的函数,使Lua更加强大了。C++中的类映射到Lua时,比较复杂,要涉及到Lua的metatable。下一篇,我们将一起探索metatable。

C++与Lua交互(四)的更多相关文章

  1. C++与Lua交互(一)

    引言 之前做手游项目时,客户端用lua做脚本,基本所有游戏逻辑都用它完成,玩起来有点不爽,感觉"太重"了.而我又比较偏服务端这边(仅有C++),所以热情不高.最近,加入了一个端游项 ...

  2. C++与Lua交互之配置&交互原理&示例

    |Lua 简介 Lua 是一种轻量小巧的脚本语言,也是号称性能最高的脚本语言,它用C语言编写并以源代码形式开放. 某些程序常常需要修改内容,而修改的内容不仅仅是数据,更要修改很多函数的行为. 而修改函 ...

  3. Win32下 Qt与Lua交互使用(四):在Lua脚本中自由执行Qt类中的函数

    话接上篇.通过前几篇博客,我们实现在Lua脚本中执行Qt类中函数的方法,以及在Lua脚本中连接Qt对象的信号与槽. 但是,我们也能发现,如果希望在Lua脚本中执行Qt类的函数,就必须绑定一个真正实现功 ...

  4. C++与Lua交互(二)

    上一篇我们搭建好了整个的项目环境,现在,我们一起探索一下如何将lua寄宿到C++中. 宿主的实现 我们在LuaWithCPPTest项目下,查看Source.cpp代码如下: #include < ...

  5. C++与Lua交互(三)

    通过上一篇的热身,我们对C++调用lua变量有了一个认识,现在让我们再深入一点,去探索一下如何调用lua的函数.表. Lua与宿主通讯的关键--栈 lua是个动态脚本语言,它的数据类型如何映射到C++ ...

  6. 最优雅的C++跟lua交互.

    我先来吐槽一下我们这个项目. 我是做手机游戏的, cocos2dx引擎, lua编码. 这本来是一件很欢快的事情, 因为不用接触C++. C++写久了的人写lua, 就会感觉任督二脉被打通了, 代码写 ...

  7. C++与lua交互

    项目开发的脚本层用的是Lua,引擎用的是C++.但是经理不给开放引擎层的代码.刚好最近项目空闲,安排了学习C++跟Lua的通信. 一.C++与Lua数据交互 数据交互主要是通过C API来实现 首先, ...

  8. cocos2d-x - C++/Lua交互

    使用tolua++将自定义的C++类嵌入,让lua脚本使用 一般过程: 自定义类 -> 使用tolua++工具编译到LuaCoco2d.cpp中 -> lua调用 步骤一:自定义一个C++ ...

  9. Linux下C/C++和lua交互-Table

    本来这些文章都是在我的个人网站www.zhangyi.studio,目前处在备案状态,暂时访问不了,所以搬到这边.  最近这两天需要弄清楚C++和lua间相互调用和数据传递,废话不多说,直接上过程. ...

随机推荐

  1. ssh连接阿里云一段时间不操作自动断开

    打开/etc/ssh/sshd_config 添加或修改: ClientAliveInterval 120 ClientAliveCountMax 0

  2. MYSQL SQL 审核工具 (inception安装步骤)

    http://blog.csdn.net/wulantian/article/category/5825391

  3. 关于dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent的分发机制浅析

    虽说这个问题不是很难...动动手就能看出答案...但是似乎不太容易理解...几次尝试把这个问题说明白....但是好像感觉说不明白....(顿时想起了那句话----说不明白就是自己还不明白! 我怎么可能 ...

  4. Redis 字符串(String)

      Redis 字符串数据类型的相关命令用于管理 redis 字符串值,基本语法如下: 语法 redis 127.0.0.1:6379> COMMAND KEY_NAME 实例 redis 12 ...

  5. MDI/MDIX接口

    转载:http://blog.chinaunix.net/uid-24148050-id-137067.html MDI/MDIX is a type of Ethernet port connect ...

  6. windows系统中eclipse C c++开发环境的搭建

    搜索了网上各种版本的eclipse c,c++ ,头都晕了..以前的版本很多都失效了..下面这个是搞了两天才调通的.超级强大,推荐 1.先装MinGW 2.配置环境变量3.然后把安装目录\mingw\ ...

  7. App安全之网络传输安全

    移动端App安全如果按CS结构来划分的话,主要涉及客户端本身数据安全,Client到Server网络传输的安全,客户端本身安全又包括代码安全和数据存储安全.所以当我们谈论App安全问题的时候一般来说在 ...

  8. Fence Repair

    有n(n>=1&&n<=20000)个木棒.现在要将这些木棒还原为一根.每次只能将两根连接成一根.费用为这两根的长度.求还原的最小费用. 输入:n,接下来n个正整数,代表长 ...

  9. python 调用zabbix api接口实现主机的增删改查

    python程序调用zabbix系统的api接口实现对zabbix_server端主机的增删改查,使用相关功能时候,需要打开脚本中的相关函数. 函数说明: zabbixtools()  调用zabbi ...

  10. css笔记14:css文件之间可以相互引用

    css文件之间相互引用是通过@import指令完成的 格式: @import  url("被引用的css文件"); 顺便说一句,如果希望在html或者php文件中引用某个xxx.c ...