Lua 可以作为程序库用来扩展应用的功能,也就是Lua 可以作为扩展性语言的原因
所在。同时,Lua 程序中可以注册有其他语言实现的函数,这些函数可能由C 语言(或其他语言)实现,可以增加一些不容易由Lua 实现的功能。
C 和Lua 中间有两种交互方式。
第一种,C 作为应用程序语言,Lua 作为一个库使用;
第二种,反过来,Lua 作为程序语言,C 作为库使用。这两种方式,C 语言都使用相同的API 与Lua 通信,因此C 和Lua 交互这部分称为C API。
既然C和LUA的交互是通过C API进行的,那么接下来,先介绍C API。
C API
C API分类:
1.读写Lua 全局变量的函数,
2.调用Lua 函数的函数,
3.运行Lua 代码片断的函数,
4.注册C 函数然后可以在Lua 中被调用的函数 等。
一个简单的代码DEMO
1 写一个简单的解释器,代码如下:
2 #include <stdio.h>
3 #include <lua.h>
4 #include <lauxlib.h>
5 #include <lualib.h>
6 int main (void)
7 {
8 char buff[256];
9 int error;
10 lua_State *L = lua_open(); /* opens Lua */
11 luaopen_base(L); /* opens the basic library */
12 luaopen_table(L); /* opens the table library */
13 luaopen_io(L); /* opens the I/O library */
14 luaopen_string(L); /* opens the string lib. */
15 luaopen_math(L); /* opens the math lib. */
16 while (fgets(buff, sizeof(buff), stdin) != NULL) {
17 error = luaL_loadbuffer(L, buff, strlen(buff),
18 "line") || lua_pcall(L, 0, 0, 0);
19 if (error) {
20 fprintf(stderr, "%s", lua_tostring(L, -1));
21 lua_pop(L, 1);/* pop error message from the stack */
22 }
23 }
24 lua_close(L);
25 return 0;
26 }
因此,如果你用C 方式来编译它,但用在C++中,那么你需要象下面这样来包含lua.h 头文件。
extern "C" { #include <lua.h> } 一个常用的技巧是建立一个包含上面代码的lua.hpp 头文件,并将这个新的头文件包 含进你的C++程序。
lua用一个抽象的栈在Lua 与C 之间交换值。
栈中的每一条记录都可以保存任何Lua 值。无论你何时想要 从Lua 请求一个值(比如一个全局变量的值),调用Lua,被请求的值将会被压入栈。无 论你何时想要传递一个值给Lua,首先将这个值压入栈,然后调用Lua(这个值将被弹 出)。
1. 压入元素
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,
size_t length);
void lua_pushstring (lua_State *L, const char *s);
int lua_checkstack (lua_State *L, int sz); // 检查栈是否有足够空间
2. 从栈中取值
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);
int lua_is... (lua_State *L, int index); // 判断栈中值类型
// 类型细分为: LUA_TNIL、LUA_TBOOLEAN 、LUA_TNUMBER 、LUA_TSTRING 、LUA_TTABLE 、LUA_TFUNCTION、LUA_TUSERDATA 以及LUA_TTHREAD
3. 其他栈操作
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int index);
void lua_pushvalue (lua_State *L, int index);
void lua_remove (lua_State *L, int index);
void lua_insert (lua_State *L, int index);
void lua_replace (lua_State *L, int index);
void lua_getglobal (lua_State *L, const char *name); // 将lua的全局变量放到栈顶
4. 调用lua函数
首先,将被调用的函数入栈;
第二,依次将所有参数入栈;
第三,使用lua_pcall 调用函数;
最后,从栈中获取函数执行返回的结果。
函数调用的demo
//C code
double f (double x, double y) {
double z;
/* push functions and arguments */
lua_getglobal(L, "f"); /* function to be called */
lua_pushnumber(L, x); /* push 1st argument */
lua_pushnumber(L, y); /* push 2nd argument */
/* do the call (2 arguments, 1 result) */
if (lua_pcall(L, 2, 1, 0) != 0)
error(L, "error running function `f': %s",
lua_tostring(L, -1));
/* retrieve result */
if (!lua_isnumber(L, -1))
error(L, "function `f' must return a number");
z = lua_tonumber(L, -1);
lua_pop(L, 1); /* pop returned value */
return z;
}
-- lua code
function f (x, y)
return (x^2 * math.sin(y))/(1 - x)
End
5. 调用C函数
从Lua调用C 函数我们必须注册函数,也就是说,我们必须把C 函数的地址以一个适当的方式传递给Lua 解释器。
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
第一行将类型为function 的值入栈,第二行将function 赋值给全局变量mysin。这样修改之后,重新编译Lua,你就可以在你的Lua 程序中使用新的mysin 函数了。
6.C 函数库
Lua 通过这个注册过程,就可以看到库中的C 函数。一旦一个C 函数被注册之后并
保存到Lua 中,在Lua 程序中就可以直接引用他的地址(当我们注册这个函数的时候传递给Lua 的地址)来访问这个函数了。
当你打算使用 C 函数来扩展Lua 的时候,即使你仅仅只想注册一个C 函数,将你的C 代码设计为一个库是个比较好的思想:不久的将来你就会发现你需要其他的函数。
luaL_openlib 函数接受一个C 函数的列表和他们对应的函数名,并且作为一个库在一个table 中注册所有这些函数。
首先,我们必须定义库函数
static int l_dir (lua_State *L) {
... /* as before */
}
第二步,我们声明一个数组,保存所有的函数和他们对应的名字。
static const struct luaL_reg mylib [] = {
{"dir", l_dir},
{NULL, NULL} /* sentinel */
};
第三步,我们使用luaL_openlib 声明主函数:
int luaopen_mylib (lua_State *L) {
luaL_openlib(L, "mylib", mylib, 0);
return 1;
}
第四部,你就可以在Lua 中直接使用loadlib 加载你刚才定义的函数库了,
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")