刚开始练习Lua需要一个Lua解释器来执行Lua代码,我是在Windows上学习Lua的,需要自己生成最新版本的Lua解释器。
方法见于:
http://www.lua.org/manual/5.3/readme.html Building Lua on other systems
建立一个visual studio Win32 Console Application项目,把c文件和h文件分别放在Source Files和Header Files目录中,删掉luac.c文件。启动项目时会报错,按照提示增加symbol _CRT_SECURE_NO_WARNINGS,再次启动,就可以成功生成Lua解释器。
Lua是一种嵌入式语言。即它不是一个单独运行的程序,而是一个可以链接到其他程序的库。Lua的运行从主程序开始,主程序通过C API和Lua交互,C API是一组能使C代码和Lua交互的C函数。主程序可以在Lua中注册C语言(或者其他语言)编写的新函数,Lua可以将这些新函数作为库来调用。
C和Lua有两个显著的区别:
1. C需要显式释放内存,Lua采用垃圾回收机制。
2. C使用静态类型,Lua使用动态类型。
C API的设计解决了这两个问题。C和Lua通过栈来交互。
C和Lua交互过程中需要的数据和产生的数据都在栈中,可以防止数据在使用的过程中被Lua回收掉。使用栈中的数据需要注意先使用后移除,如果弹出后再使用,则可能在使用过程中被回收。
C通过lua_pushstring/lua_pushnumber/…等方法向栈中push指定类型的值,通过lua_tostring/lua_tonumber/…等方法得到栈中指定类型的值。这样就解决了C的静态类型问题。
在这个解释器中,我们把Lua作为库来使用,首先为Lua生成一个DLL文件。
visual studio上生成和使用DLL的方法见于:
https://msdn.microsoft.com/en-us/library/vstudio/ms235636(v=vs.110).aspx
在Lua项目中搜索__declspec(dllexport),你会发现生成Lua的DLL需要添加一个symbol LUA_BUILD_AS_DLL。
有了Lua库,我们就可以编写解释器了。解释器的功能是循环读取用户输入的Lua代码并执行。
#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main(void)
{
char buf[256];
lua_State *L = luaL_newstate();
luaL_openlibs(L);
int error = 0;
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
error = luaL_loadbuffer(L, buf, strlen(buf), "input") || lua_pcall(L, 0, 0, 0);
if (error) //错误处理,打印错误消息
{
printf("%s\n", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
lua_close(L);
return 0;
}
C调用Lua方法,需要把Lua方法,参数放入栈中,然后调用lua_pcall/ lua_call…等方法。
我们准备一个Lua文件,里面定义了一个方法printName,通过C程序载入Lua文件,并调用这个printName方法。
Lua文件如下:
function printName(name)
print("hello, my name is " .. name)
end
c代码如下:
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
int main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
int error = luaL_loadfile(L, "test.lua") || lua_pcall(L, 0, 0, 0);
if (error) //错误处理,打印错误消息,退出
{
printf("%s\n", lua_tostring(L, -1));
lua_pop(L, 1);
return 1;
}
lua_getglobal(L, "printName"); //获取要调用的Lua方法
lua_pushstring(L, "xiaoming"); //Lua方法需要的参数
lua_pcall(L, 1, 0, 0);
return 0;
}
Lua调用C方法,需要在Lua的Register中注册C方法:
void lua_register (lua_State *L, const char *name, lua_CFunction f);
C方法的格式为:
typedef int (*lua_CFunction) (lua_State *L);
示例:修改Lua文件,调用C方法。
function printName(name)
if checkName(name) then --checkName为C方法
print("hello, my name is " .. name)
end
end
在C中定义checkName方法
static int checkName(lua_State *L)
{
char *name = lua_tostring(L, -1); //获取参数name
int res = strcmp(name, "GM");
lua_pop(L, 1); //弹出参数name
lua_pushboolean(L, res); //返回结果
return 1; //结果个数
}
C的主程序,在Lua中注册checkName方法,调用Lua的printName方法。
int main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_loadfile(L, "test.lua"); //不考虑出错情况
lua_pcall(L, 0, 0, 0);
lua_register(L, "checkName", checkName); //注册C函数
lua_getglobal(L, "printName");
lua_pushstring(L, "xiaoming"); //lua_pushstring(L, "GM"); 不会输出任何内容
int error = lua_pcall(L, 1, 0, 0);
if (error)
{
printf("%s\n", lua_tostring(L, -1));
lua_pop(L, 1);
return 1;
}
return 0;
}
C是面向过程的语言,C方法和Lua方法是一致的,很容易注册到Lua当中。像C++/Java/C#…面向对象的语言,很多方法存在于对象上,这些方法要如何注册呢?
《Lua游戏开发实践指南》第11章 Lua驱动的GUI介绍了一种方法,为各种对象创建一个管理器,通过type和id可以查找到任意对象。比如对象上有方法draw(…),在管理器中定义方法draw(type, id, …),该方法内部获得对象,调用对象上的draw(…)方法。最后注册到Lua中的方法是管理器的中draw方法。
KopiLuaInterface采用了另外一种做法,把一个C#对象放入Lua中,可以像在C#里一样使用这个对象。
把C#对象放入Lua容器中:
Lua lua = new Lua();
lua[“dt”] = DateTime.Now;
在Lua中使用C#对象
print(dt.GetType, type(dt))
这个C#对象以userdata的方式放入Lua容器,之所以可以在userdata上调用GetType之类的方法,是因为这个userdata定义了元方法__index。
参考资料
《Lua程序设计》第二版
《Lua游戏开发实践指南》
Lua 5.3参考手册:http://www.lua.org/manual/5.3/
KopiLuaInterface:https://github.com/gfoot/kopiluainterface
原文:http://blog.csdn.net/darwinchina/article/details/43991527