C++中引入lua环境搭建
- 第一种: 联合的方式(不方便)
- 下载lua5.3.1 tar.gz
- 创建控制台项目, 静态库, 取消预编译头;
- C/C++>常规>附加包含目录: 加入5.3.1\src
- 编译得到lublib.lib
- 创建新工程
- 在vc++目录里添加包含目录 和库目录;
- 连接器里添加lualib.lib
- 编译; 如果失败则拷贝lualib.lib到根目录dubug下;
- OK
- 第二中: 生成静态库
- 新建控制台项目, 静态库, 取消预编译头;
- 将5.3.2的src中添加到项目中, 但 不要添加lua.c
- 编译得到xxx.lib, 这就是所得到的静态库
- 建立一个文件夹如lua5.3,分别在子文件夹lib里放入刚生成的xxx.lib(可以改名为lua5.3.2.lib)和include子文件夹里放入lua头文件: lauxlib.h, lua.h, luaconf.h, lualib.h
- 新建C++项目, 在”项目属性>VC++目录>包含目录和库目录里”添加上面的lib和include路径;
- “项目属性>连接器>输入”里添加lua5.3.2.lib
- OK
#include <stdio.h>
#include <string.h>
extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
int main(int argc, char* argv[])
{
lua_State *L = luaL_newstate();
luaL_openlibs(L); // 加载Lua通用扩展库
if(luaL_loadfile(L,"test.lua"||lua_pcall(L,0,0,0)) //或luaL_dofile(L,"test.lua")
printf("error pcall!: %s\n",lua_tostring(L,-1));
// 前面搭建了运行环境,lua代码写在了test.lua文件中
// ......
//
lua_close(L);
return 0;
}
C++调用Lua函数
要在C++中调用lua函数,则有如下函数可以利用:
lua_getglobal()
就是从lua中取得函数,压入栈中;随后压入函数的参数;
如在test.lua中有如下代码:
function he(x,y)
return x*y
end
则的C++中的调用过程是:
//.....
lua_getgloabl(L,"he");
lua_pushnumber(L,5);
lua_pushnumber(L,6);
// run the lua program
// lua_pcall(L,nargs,nresults,0)
if(lua_pcall(L,2,1,0) != 0)
printf("error pcall!: %s\n",lua_tostring(L,-1));
// if error then push errorinfo in the stack else push reuslts
if(!lua_isnumber(L,-1))
printf("error return!\n");
int re = (int)lua_tonumber(L,-1);
函数参数、返回值压栈是正序压栈;如果有错误发生的话,
lua_pcall
会捕获它,然后把单一的值(错误信息)压入堆栈,然后返回错误码。lua_pcall
总是把函数本身和它的参数从栈上移除。
Lua中调用C++函数
- 要写一个能让Lua调用的C函数,就要符合
lua_CFunction
定义:typedef int (*lua_CFunction) (lua_State *L); //return 返回值的个数
- 当Lua调用C函数的时候,同样使用栈来交互。C函数从栈中获取她的参数,调用结束后将结果放到栈中,并返回放到栈中的结果个数。
- 这儿有一个重要的概念:用来交互的栈不是全局栈,每一个C函数都有他自己的私有栈。当Lua调用C函数的时候,第一个参数总是在这个私有栈的
index=1
的位置。
如:函数he
// 普通函数
static int he(lua_State* L)
{
// 从栈中检查参数是否合法并读取参数,
int a = luaL_checknumber(L,1);
int b = luaL_checknumber(L,2);
int re = a*b;
// 将运算结果返回栈中供lua使用
lua_pushnumber(L,re);
return 1;
}
// 在C++中向lua传递table结构的数据
// 这里的table是:{{"he"},{"li"}}
int TTable(lua_State* L)
{
// 创建大table
lua_newtable(L,1);
// 大table的key
lua_pushnumber(L,1); // 1为键
//第一个小table
lua_newtable(L);
// 第一个小table的key,value
lua_pushnumber(L,1);
lua_pushstring(L,"he");
lua_settable(L,-3);
//第一个小table的成员结束
lua_settable(L,-3);
//第二个小table,类似上面的过程
lua_pushnumber(L,2); // 2为键
lua_newtable(L,2);
lua_pushnumber(L,1);
lua_pushstring(L,"li");
lua_settable(L,-3);
lua_settable(L,-3)
return 1; //1个大table
}
在main中可以使用lua_dostring()
,lua_loadfile()
,lua_pcall(L,0,0,0)
来执行lua代码:
int main(int argc, char* argv[])
{
lua_State *L = luaL_newstate();
luaL_openlibs(L); // 加载Lua通用扩展库
// 将he函数注册成lua的全局函数
lua_register(L,"he",he);
if(luaL_loadfile(L,"test.lua")/*||lua_pcall(L,0,0,0)*/)
printf("error pcall!: %s\n",lua_tostring(L,-1));
lua_close(L);
return 0;
}
.lua
文件文间之间的全局变量调用
比如有两个文件
//a.lua
a = 50
local b = 10
//h.lua
dofile("X:/.../a.lua")
//或在同一个目录下时:doflie("a.lua"),require会更好
print(a,b)
lua中:
h.lua
想要调用a.lua
中的内容则要在a.lua
中运行dofile("X:/.../a.lua")
C++中两种方法:
- 如上面所示, 在
h.lua
中使用dofile(“a.lua”), 文件放同一个目录;- 或在C代码加载时先用
luaL_dofile(L,"a.lua");
再luaL_dofile(L,"h.lua");
顺序不能变;
引入C模块到 lua
luaL_register
这个函数接收一些C函数及其名称,并将这些函数注册到一个与模块同名的table
中。假设创建一个模块,其中包含了这个luaglue
函数。首先,必须定义这个模块函数:
static int luaglue(lua_state *L)
{
}
然后,声明一个数组,其中包含模块中所有函数及名称。这个数组元素的类型为luaL_Reg
结构,该结构有两个字段,一个字符串和一个函数指针:
static const struct luaL_Reg mylib[] = {
{"dir",l_dir},
{NULL,NULL}//结尾
};
最后,声明一个主函数,其中用到了luaL_register
:
int luaopen_mylib(lua_State *L)
{
luaL_register(L,"mylib",mylib);
return 1;
}
其中luaL_register
原型为:
void luaL_register (lua_State *L,const char *libname,const luaL_Reg *l);
luaL_register
根据给定的名称(“mylib”)创建(或复用)一个table
,并用数组mylib
中的信息填充这个table
。在luaL_Register
返回时,会将这个table
留在栈中。最后,luaopen_mylib
函数返回1,表示将这个table
返回给Lua。
打开一个库,当
libname
为null时,该函数注册所有在luaL_Reg
上的函数,不为null时,该函数会创建一个table
,根据libname注册不与libname
关联的函数。
- 当写完c模块后,必须将其链接到解释器。如果Lua解释器支持动态链接的话,那么最简便的方法是使用动态链接机制。在这种情况中,必须将c代码编译成动态链接库,并将这个库放入C路径(
LUA_CPATH
)中。然后,便可以用require从Lua中加载这个模块:
require "mylib"
这个调用会将动态库mylib
链接到Lua,并会寻找luaopen_mylib
函数,将其注册为一个Lua函数,然后调用它以打开模块。
如果解释器不支持动态链接,那么就必须用新的模块来重新编译Lua。此外,还需要以某种方式来告诉解释器,它应在打开一个新状态的同时打开这个模块。最简单的做法是,将luaopen_mylib
加到luaL_openlibs
会打开的标准库列表中,这个列表在文件linit.c中。
从C++程序员的观点来看,Lua像一个“黑盒子”,为一些服务处理命令和调用。Lua通常作为最上层接口直接和程序使用者和游戏玩家打交道,在核心程序处理之前接受并响应输入。
lua5.3 C++注册函数或模块到lua
- 上面的那个luaL_register函数在lua5.2以后就没有用了,所以一直在找5.2以后版本的用法, 网上找了半天都没有指出重点,最后在下面的网站上找到;
http://acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/
结合下面两篇介绍后这里给出一些理解:
http://www.linuxidc.com/Linux/2014-05/102528p2.htm
http://blog.163.com/cqit_jsj/blog/static/65127220127251231881/
//要注册的lua里的C++函数的写法这里就不说了,随便写一个
static int f(lua_State* L)
{
printf("hello\n");
return 0;
}
// .... 还可以继续添加
//注册函数数组
const luaL_Reg mylib[] =
{
{"myf",f},
//{//可以继续添加},
{NULL,NULL}
}
//定义一个库打开函数
static luaopen_mylib(lua_State* L)
{
//lua5.1 是luaL_register(L,"mylib",mylib)来完成
// lua5.2以上是
lua_newlib(L,mylib);
//或者等效为下面
luaL_newlibtable(L,mylib);
lubL_setfuncs(L,func,0);
return 1;
}
//如果有多个库可以在用个库数组来进行注册如下
// 可不用
const luaL_Reg lualibs[]=
{
{"mylib",luaopen_func}, //打开库函数
{/*其它库*/},
{NULL,NULL},
}
到这里,我们可以选择去将库导出为.dll来在lua中调用,这个网上好多资料,在lua中要用local mylib = require "mylib"
来获取.dll文件, 参考
http://blog.csdn.net/ljhjason/article/details/28860633
这里选择不导出为.dll所以进行如下操作:
int main
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
// 打开自己的库如果用了lublibs库数组则用循环打开
luaL_requiref(L,"mylib",luaopen_mylib,1);
lua_pop(L,1);
luaL_dofile("xxx.lua");
....
}
注册C++类到lua
http://blog.csdn.net/siddontang/article/details/2316547
C++ struct对应到lua table
// 压入表的键值对函数(没有进行栈检测)
inline void PushTableKV(lua_State* L, const char* k, int v)
{
lua_pushstring(L,k);
lua_pushnumber(L,v);
lua_settable(L,-3);
}
// 使用时候
{
lua_newtable(L)
PushTableKV(L,"v1",10);
....
}