先说从Lua调用C代码。
Lua调用C
Lua调用C,就是用Lua的C API写一个动态链接库,给Lua程序用。下面看个简单的样例,用C写了个adder函数,返回一个C closure。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <lua.h> #include <lauxlib.h> #include <stdio.h>
static int _do_adder(lua_State* L) { double base = lua_tonumber(L, lua_upvalueindex(1)); double to_add = luaL_checknumber(L, 1); lua_pushnumber(L, base + to_add);
return 1; }
static int _adder(lua_State* L) { double base = luaL_checknumber(L, 1); lua_pushvalue(L, 1); lua_pushcclosure(L, _do_adder, 1); return 1; }
int luaopen_add(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { {"adder", _adder}, {NULL, NULL}, }; luaL_newlib(L,l); luaL_setfuncs(L, l, 0); return 1; }
|
编译这个so的Makefile:
1 2 3 4 5 6 7 8
| CFLAGS = -g -O0 -Wall -I. -I /usr/local/include LDFLAGS = -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup
all: add.so
add.so: lua-hello.c $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ {% endhighlight %}
|
生成add.so
后,可以从lua代码里调用了:
1 2 3
| c = require "add" local addTwo = c.adder(2) print(addTwo(3))
|
add.so的入口在luaopen_add()
,在require(“add”)时,就会加载add.so,并执行luaopen_add()
,这个函数将_adder()
注册到lua。返回了一个table(Lua里一切都是table), table里有一个adder()
函数。
C, Lua混编,用一个Stack来交换数据,上面用到的 luaL_checknumber()
lua_pushvalue()
第二参数都是这个Stack的索引。
Lua只能调用lua_CFunction类型的C函数:
1
| typedef int (*lua_CFunction) (lua_State *L);
|
C函数从L里取参数,处理,将结果push进L。
Lua和C最大的不同在于:Lua无类型,Lua有GC;此外,还得留心错误处理。
实际使用时,如果add.so不在默认的搜索路径里,可以设置LUA_CPATH
,这个环境变量的格式参见 Lua手册。
Lua调用C++ – wrapper
Lua的api是C,文档,书上也都是在说怎么在Lua里调用C,很少谈到Lua调用C++,C++对象和Lua对象的方法,变量没法一一对应。要想从Lua里调用C++的成员方法,得写一层C的wrapper, 有点lua调用C,C调用C++的意思。
还是来看个实例,我们有一个C++类:
1 2 3 4 5 6 7 8 9
| #include <iostream> class Sum { public: double sum(double a, double b) { return a + b; } };
|
很简单,就一个成员方法,给两个 double
,返回他们的和。
为了在Lua里使用这个类,先来写一个so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| #include "Sum.h" #include <lua.hpp>
extern "C" { static int Sum_sum(lua_State* L); static int Sum_new(lua_State* L); int luaopen_sum(lua_State* L); }
static int Sum_sum(lua_State* L) { Sum** ud = (Sum**)lua_touserdata(L, 1); double a = luaL_checknumber(L, 2); double b = luaL_checknumber(L, 3); double c = (*ud)->sum(a, b); lua_pushnumber(L, c);
return 1; }
static int Sum_new(lua_State* L) { Sum** ud = static_cast<Sum**>(lua_newuserdata(L, sizeof(Sum*))); *ud = new Sum; luaL_getmetatable(L, "Sum"); lua_setmetatable(L, -2);
return 1; }
static int Sum_index(lua_State* L) { lua_settop(L, 2); lua_getmetatable(L, -2); lua_pushvalue(L, -2); lua_gettable(L, -2);
return 1; }
int luaopen_sum(lua_State* L) { luaL_checkversion(L);
const luaL_Reg table[] = { {"new", Sum_new}, {NULL, NULL} }; const luaL_Reg metatable[] = { {"sum", Sum_sum}, {"__index", Sum_index}, {NULL, NULL} };
lua_newtable(L); luaL_setfuncs(L, table, 0);
lua_setglobal(L, "Sum");
luaL_newmetatable(L, "Sum"); luaL_setfuncs(L, metatable, 0); lua_pop(L, 1);
return 0; }
|
这个so做的事就是,定义了一个Lua的全局table Sum
,将类的构造函数,static方法放进去,再将类的实例方法放到一个叫做”Sum”的metatable里。
构造函数new一个C++的Sum类,将指针存到lua里的userdata, 并为这个新生成的userdata指定metatable.
编译这个so的Makefile:
1 2 3 4 5 6 7 8 9
| CFLAGS = -g -O0 -Wall -I. -I /usr/local/include LDFLAGS = -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup LIBS = -ldl -llua CXX = g++
all: sum.so
sum.so: lua-Sum.cpp $(CXX) $(CFLAGS) $(LDFLAGS) $^ -o $@
|
在Lua里测试一下:
1 2 3 4 5 6 7 8 9
| require("sum")
local abc = Sum.new() print(abc) local mt = getmetatable(abc) for k,v in pairs(mt) do print(k, v) end print(abc:sum(40, 2))
|
上面的例子太过简单,而且很多地方是ad hoc,如果要在真实C++项目里使用Lua, 可以考虑使用一些方便的C++ Lua绑定库,这里有一个这类库的列表。上面实例主要参照其中的luawrapper库实现。
这里只说到lua调用C/C++, 真实的项目里,一般是C/C++(host程序)调用Lua(脚本), Lua里再调用C (给Lua写的so), so里再调用lua(callback函数)。