【lua】使用 tolua 实现 lua 与 c++ 交互
2017-09-29 21:22
1221 查看
tolua 简介
tolua 也叫 tolua++,是一个第三方库,简化了 lua 访问 c/c++ 的工作。tolua 自动导出 c/c++ 中的常量、变量、函数、类等,提供给 lua 直接访问和调用。lua 与 c 是可以直接相互访问的,不使用 tolua 或其它第三方库也可以实现 lua 调用 c/c++,但比较麻烦,开发效率太低。lua 直接访问 c/c++ 可以参考我这篇文章
lua 访问 c
可以看到无论是在 c 程序中创建一个 lua 环境来执行 lua 代码,还是导出一个 c 模块供 lua 程序使用;要让 lua 能访问 c 函数,必须将手动将 c 函数注册到 lua 环境中;另外,c 函数参数和返回值都需要手动操作栈。这个效率是很慢的,最好的办法就是像写普通 c 代码一样,然后将 c 程序导出成 lua 可以直接调用的代码,tolua 就是干这个事的。
tolua 官网:http://webserver2.tecgraf.puc-rio.br/~celes/tolua/tolua-3.2.html#using
tolua 下载地址:https://github.com/LuaDist/tolua
使用 tolua
从 github 下载的 tolua 是源代码,必须先编译,编译之后我们将得到两个文件 tolua.exe 和 tolua.lib。tolua.exe 更像一个工具,负责从包文件(.pkg) 中生成 .c 文件或 .cpp 文件,这个 c/c++ 源文件就是导出的可供 lua 调用的代码。tolua.lib 则是 tolua 源码编译后的库,要在程序中使用 tolua,则必须包括这个库文件。tolua 的编译使用 cmake 来编译,打开 cmake gui,然后选择 tolua 的根目录,再选择生成项目的存放路径,然后点击 Configure,选择要使用的 ide(windows 上一般为 visual studio),然后点击 Generate 就会生成相应的项目文件。具体的就不详说了,网上有很多教程。生成项目之后直接打开 vs 解决方案,生成所有项目即可。
编译完成之后,共有下面几个文件
* tolua.exe 用于从 .pkg 文件生成 .c/.cpp 文件
* tolua.dll 动态链接库文件
* tolua.lib 动态链接库对应的 lib 文件,如果是使用静态链接库项目生成的 lib 文件,则只有一个 lib 文件,没有 dll 文件
* tolua.h 头文件,在源代码的 include 目录下
使用 tolua 有三步,
第一步,正常编写 c/c++ 代码,然后编写对应的 package 文件,package 文件语法基本与 .h 文件一样,注意下面几点即可
* 不能有 public,private 等作用域修饰符,只能导出共有的成员,默认就是 public
* 函数原型和头文件保持一致,包括函数名参数和返回值
* 虚函数不能导出
* 在文件头使用
$#include "test.h"包含头文件
第二步,使用 tolua.exe 从 package 文件生成相应的 c/c++ 源文件。在 tolua.exe 所有目录打开命令行,然后输入
tolua -n [mylib] -o [test.cpp] test.pkg,-n 参数指明这个文件所在的包,如果不写的话就默认跟文件名一样,-o 指明生成的源文件名,这个参数是必须的,最后就是源 package 文件。因为我生成的 tolua.exe 是依赖于动态链接库的,所以要把 tolua.dll 放在 tolua.exe 目录下,如果是使用静态链接库生成的 tolua.exe 则不用。为了方便起见,也可以将 tolua.exe 添加在环境变量,把 tolua.exe 所在的路径添加到 path 变量中去即可。
第三步,在项目中使用 tolua 生成的文件,这里的文件包括使用 c/c++ 写的原始代码,使用 tolua 导出的 c/c++ 源文件。导出的源文件中有一个方法
tolua_mylib_open,其中 mylib 就是上面 -n 参数指定的包名。使用之前要先在 c++ 层创建 lua 环境之后调用 tolua_mylib_open 函数,之后 lua 代码就可以访问 c++ 的内容了,访问的时候是通过 tolua 调用到上面导出的源文件,然后再调用到原始的实现文件。
导出源文件
上面讲了使用 tolua 的三部曲,下面使用实际例子讲解一下。首先,正常编写 c++ 代码,这里我们创建一个类原始头文件
//mylib.h class Test { public: Test(int a, int b); ~Test(); void sayHello(); int add(); int getA(); private: int a; int b; };
原始源文件
//mylib.cpp #include "mylib.h" #include <iostream> Test::Test(int a, int b) { this->a = a; this->b = b; } Test::~Test() { } void Test::sayHello() { std::cout << "hello world" << std::endl; } int Test::add() { return this->a + this->b; } int Test::getA() { return this->a; }
package 文件
$#include "mylib.h" class Test { Test(int, int); ~Test(); void sayHello(); int add(); int getA(); };
可以看到 package 文件和 c++ 头文件基本一致,要注意的是要在文件头引入头文件,然后把 public 关键字去掉。所有公有的函数或数据都可以导出,如果不想导出某个函数,则在 package 文件中不要定义就可以了。
然后打开命令行,输入下面的命令
tolua -n mylib -o tolua.cpp mylib.pkg
导出的文件名可以任意命名,但因为我们已经有一个原始的源文件 mylib.cpp 了,所以这里不能将导出的源文件命名为 mylib.cpp,否则后面使用的时候就会有问题,这里我命名为 tolua.cpp。还有一个要注意的就是必须把 mylib.h 跟 mylib.pkg 放在一起,因为 package 文件需要用到头文件(第一行就已经引入头文件了)
现在我们有了 mylib.h、mylib.cpp 和 tolua.cpp 这三个文件,接下来就可以在项目中使用了
在 c++ 程序中使用 tolua
新建一个 c++ 控制台程序,因为要使用到 lua 和 tolua,所以要把相应的头文件和库文件包含进来,头文件有 lua.h、lualib.h、lauxlib.h、lua.hpp、luaconf.h 和 tolua.h,库文件则有 lua.lib 和 tolua.lib。首先,我们先来测试一下 lua 环境,新建一个 main.cpp 文件,输入下面代码
//main.cp 4000 p #include <iostream> extern "C" { #include "lualib.h" #include "lauxlib.h" } using namespace std; int main() { int tolua_mylib_open(lua_State*); lua_State* state = luaL_newstate(); luaL_openlibs(state); if (luaL_dostring(state, "print([[hello world]])") != 0) { cout << "excute lua file failed!" << endl; } lua_close(state); system("pause"); return 0; }
如果正确打印出 “hello world”,则说明 lua 环境没有问题,否则就检查一下头文件和库文件是否正确引入了
接下来把 mylib.h、mylib.cpp 和 tolua.cpp 这三个文件添加到项目中,我们先来分析下 tolua.cpp 这个文件的内容
static int tolua_collect_Test (lua_State* tolua_S){} static void tolua_reg_types (lua_State* tolua_S){} //对应构造函数 static int tolua_mylib_Test_new00(lua_State* tolua_S){} //对应析构函数 static int tolua_mylib_Test_delete00(lua_State* tolua_S){} //sayHello static int tolua_mylib_Test_sayHello00(lua_State* tolua_S){} //add static int tolua_mylib_Test_add00(lua_State* tolua_S){} //getA static int tolua_mylib_Test_getA00(lua_State* tolua_S){} LUALIB_API int luaopen_mylib (lua_State* tolua_S){} TOLUA_API int tolua_mylib_open (lua_State* tolua_S){}
这里只列出主要的几个函数,可以看到 tolua 在导出 c++ 源码的时候为每个函数都生成一个对应的静态函数;除此之外,还有几个重要的函数,tolua_collect_Test 用于垃圾回收,tolua_reg_types 用于注册类名,toluaopen_mylib 用于打开库函数,tolua_mylib_open 用于打开 tolua,这是我们唯一关心的函数,在使用 tolua 之前必须先调用这个函数,这样所有导出的 c++ 函数就可以在 lua 中使用了
接下来开始测试在 lua 中访问 c++ 函数,新建一个 test.lua 文件,输入下面代码
local test = Test:new(1, 2) test:sayHello() print("a = " .. test:getA()) print("a + b = " .. test:add())
然后修改 main.cpp 文件
#include <iostream> extern "C" { #include "lualib.h" #include "lauxlib.h" } #include "mylib.h" using namespace std; int main() { int tolua_mylib_open(lua_State*); lua_State* state = luaL_newstate(); luaL_openlibs(state); tolua_mylib_open(state); if (luaL_dofile(state, "scripts/test.lua") != 0) { cout << "excute lua file failed!" << endl; lua_close(state); return 1; } lua_close(state); system("pause"); return 0; }
首先,要引入 mylib.h 头文件,然后调用
tolua_mylib_open(state);打开 tolua,这个函数在 tolua.cpp 文件中定义和实现,main.cpp 并不知道这个函数,所以要在使用前手动定义一下
int tolua_mylib_open(lua_State*);,之后就可以执行 test.lua 文件了
如果没错误,将会看到下面的执行结果
在 lua 程序中使用 tolua
这种方式其实和在 c++ 程序中使用 tolua 没什么区别,只是我们测试的项目是 c++ 项目还是 lua 项目而已。要在纯 lua 程序中使用 tolua,首先得导出一个 c++ 模块。新建一个 c++ 动态链接库项目,同样地将 lua 和 tolua 的头文件和库文件包含进来,还有 mylib.h、mylib.cpp 和 tolua.cpp 这三个文件也添加进项目来。导出 c 模块给 lua 使用同样可参考我这篇文章
lua 访问 c
导出 c++ 模块,在模块内注册一个函数,在这个函数里打开 tolua,然后 tolua 绑定的所有 c++ 函数就可以在 lua 中使用了。我们创建一个 lib.h 和 lib.cpp 文件,用于导出 c++ 模块
//lib.h #pragma once #include "lua.h" extern "C" { __declspec(dllexport) int luaopen_test(lua_State* L); }
//lib.cpp #include "mylib.h" extern "C" { #include "lib.h" #include "lualib.h" #include "lauxlib.h" } static int export_tolua_test(lua_State* L) { int tolua_mylib_open(lua_State* L); tolua_mylib_open(L); return 1; } static const luaL_Reg toluaTest[] = { { "export_tolua_test",export_tolua_test }, { NULL,NULL } }; extern "C" { __declspec(dllexport) int luaopen_test(lua_State* L) { luaL_newlibtable(L, toluaTest); luaL_setfuncs(L, toluaTest, 0); return 1; } }
编译之后得到一个 lib.dll 文件,在 lua 代码中加载这个动态链接库文件,得到一个模块,这个模块里面有
export_tolua_test这个函数,调用这个函数就会执行
tolua_mylib_open(L);,从而打开 tolua,则在 mylib 中导出的所有 c++ 东西都可以在 lua 中使用了
-- main.lua -- 加载 c++ 模块 local test = require("test") -- 调用 c++ 模块注册的函数,这个函数会打开 tolua test.export_tolua_test() -- 调用 tolua 导出的类、函数等 local t = Test:new(10, 20) t:sayHello() print(t:getA()) print(t:add())
使用 lua 解释器运行该代码,看到下面的执行结果
相关文章推荐
- 在C++使用LUA交互,LUA实现闭包,C++/LUA相互闭包
- 使用tolua++实现C++与LUA相互调用
- 使用tolua++实现C++与LUA相互调用
- 使用tolua++实现C++与LUA相互调用
- 使用tolua++实现C++与LUA相互调用
- 使用tolua++工具在Lua中使用C++自定义类
- 使用cocos2d-x 3.x lua 开发时 lua与c++交互
- Lua学习之3 :tolua++导出C++变量给Lua使用
- [lua] 使用lua string作为二进制buffer和c/c++交互
- (使用lua++)Lua脚本和C++交互(三)
- lua知识点1-使用lua string作为二进制buffer和c和c++交互
- lua 与 c/c++ 交互(6) lua调用C++(使用数组 和字符串函数)
- 使用tolua++创建基于C/C++语言的lua脚本
- VS2012实现C++与Lua交互
- Quick-Cocos2d-x 使用tolua工具导出C++的类给Lua调用
- tolua 使用 Lua调用c++多返回值函数
- (使用lua++)Lua脚本和C++交互(四)
- Quick-Cocos2d-x 使用tolua工具导出C++的类给Lua调用
- Quick-Cocos2d-x 使用tolua工具导出C++的类给Lua调用
- Quick-Cocos2d-x 使用tolua工具导出C++的类给Lua调用