programing in lua 第二版 中文版 总结
示例程序
#include <stdio.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main (void)
{
char buff[256];
int error;
lua_State *L = lua_open(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */
while (fgets(buff, sizeof(buff), stdin) != NULL) {
error = luaL_loadbuffer(L, buff, strlen(buff),
"line") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1);/* pop error message from the stack */
}
}
lua_close(L);
return 0;
}
函数 lua_open 创建一个新环境(或 state)。lua_open 创建一个新的环境时,这个环
境并不包括预定义的函数,甚至是 print。为了保持 Lua 的苗条,所有的标准库以单独的
包?供,所以如果你不需要就不会强求你使用它们。头文件 lualib.h 定义了打开这些库
的函数。例如,调用 luaopen_io,以创建 io table 并注册 I/O 函数(io.read,io.write 等等)
到 Lua 环境中。
堆栈
C 和 lua之间的数据交互,是通过堆栈来实现的。
设计原因:解决C与lua通信问题
- 动态与静态类型系统的不匹配
- 自动与手动内存管理的不一致
相关操作API
压栈的函数
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);
查询函数
API 用索引来访问栈中的元素。bottom=1,top = n ; bottom = -n,top = -1
int lua_is... (lua_State *L, int index); --是否能被转换成指定的那种类型
从栈取值
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);
Lua_tostring 函数返回一个指向字符串的内部拷贝的指针。
只要这个指针对应的值还在栈内,Lua 会保证这个指针一直有效。
其 他 堆 栈 操作
int lua_gettop (lua_State *L); return #stack
void lua_settop (lua_State *L, int index); 将堆栈中某个索引设置为栈顶,“小清大补”
void lua_pushvalue (lua_State *L, int index); 将堆栈中某个索引的拷贝设置为栈顶
void lua_remove (lua_State *L, int index); remove index
void lua_insert (lua_State *L, int index); 移动栈顶到制定位置
void lua_replace (lua_State *L, int index); 用栈顶替换某个index
stack操作示例
int main (void) {
lua_State *L = lua_open();
lua_pushboolean(L, 1); lua_pushnumber(L, 10);
lua_pushnil(L); lua_pushstring(L, "hello");
stackDump(L);
/* true 10 nil `hello' */
lua_pushvalue(L, -4); stackDump(L);
/* true 10 nil `hello' true */
lua_replace(L, 3); stackDump(L);
/* true 10 true `hello' */
lua_settop(L, 6); stackDump(L);
/* true 10 true `hello' nil nil */
lua_remove(L, -3); stackDump(L);
/* true 10 true nil nil */
lua_settop(L, -5); stackDump(L);
/* true */
lua_close(L);
return 0;
}
tips
- 当你调用 Lua 时,它只会改变栈顶部分;C则更灵活。
- 无论你何时压入一个元素到栈上,你有责任确保在栈上有空间来做这件事情。LUA_MINSTACK=20
- 当一个 C 函数返回后,Lua 会清理他的栈
在C中调用lua代码
加载lua文件
lua代码文件
XX.lua = {
width = 111,
height = 222,
}
C代码:
-- 引入lua库
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void load (char *filename, int *width, int *height) {
-- 创建lua解析环境
lua_State *L = lua_open();
luaopen_base(L);
luaopen_io(L);
luaopen_string(L);
luaopen_math(L);
-- 加载lua代码
if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
error(L, "cannot run configuration file: %s",lua_tostring(L, -1));
-- 获取lua的执行结果
-- lua代码声明了两个全局变量,通过这个操作把它们压到堆栈里;
lua_getglobal(L, "width");
lua_getglobal(L, "height");
-- 校验width
if (!lua_isnumber(L, -2))
error(L, "`width' should be a number\n");
-- 校验height
if (!lua_isnumber(L, -1))
error(L, "`height' should be a number\n");
-- 赋值
*width = (int)lua_tonumber(L, -2);
*height = (int)lua_tonumber(L, -1);
lua_close(L);
}
小结
之前看堆栈的时候,总是不知道怎么去理解好;感觉应该从通信开始讲,再讲原理会好一些。
调用lua函数
流程:首先,将被调用的函数入栈;第二,依次将
所有参数入栈;第三,使用 lua_pcall 调用函数;最后,从栈中获取函数执行返回的结果。
lua:
function f (x, y)
return (x^2 * math.sin(y))/(1 - x)
end
c:
加载lua文件的代码参考上文
/* call a function `f' defined in Lua */
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;
}
高级例子:
用函数名、参数说明作为参数来自动完成调用函数时的全部工作
#include <stdarg.h>
void call_va (const char *func, const char *sig, ...) {
va_list vl;
int narg, nres; /* number of arguments and results */
va_start(vl, sig);
lua_getglobal(L, func); /* get function */
/* push arguments */
narg = 0;
while (*sig) { /* push arguments */
switch (*sig++) {
case 'd': /* double argument */
lua_pushnumber(L, va_arg(vl, double));
break;
case 'i': /* int argument */
lua_pushnumber(L, va_arg(vl, int));
break;
case 's': /* string argument */
lua_pushstring(L, va_arg(vl, char *));
break;
case '>':
goto endwhile;
default:
error(L, "invalid option (%c)", *(sig - 1));
}
narg++;
luaL_checkstack(L, 1, "too many arguments");
}
endwhile:
/* do the call */
nres = strlen(sig); /* number of expected results */
if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
error(L, "error running function `%s': %s",
func, lua_tostring(L, -1));
/* retrieve results */
nres = -nres; /* stack index of first result */
while (*sig) { /* get results */
switch (*sig++) {
case 'd': /* double result */
if (!lua_isnumber(L, nres))
error(L, "wrong result type");
*va_arg(vl, double *) = lua_tonumber(L, nres);
break;
case 'i': /* int result */
if (!lua_isnumber(L, nres))
error(L, "wrong result type");
*va_arg(vl, int *) = (int)lua_tonumber(L, nres);
break;
case 's': /* string result */
if (!lua_isstring(L, nres))
error(L, "wrong result type");
*va_arg(vl, const char **) = lua_tostring(L, nres);
break;
default:
error(L, "invalid option (%c)", *(sig - 1));
}
nres++;
}
va_end(vl);
}
lua调用C函数
从 Lua调用 C 函数我们必须注册函数,也就是说,我们必须把 C 函数的地址以一个适当的方式
传递给 Lua 解释器。
调用过程也是通过堆栈交互,用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。
C 函数从栈中获取lua传过来的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个 C 函数还会返回结果的个数。
任何在 Lua 中注册的函数必须有同样的原型,这个原型声明定义就是 lua.h 中的
lua_CFunction:
typedef int (*lua_CFunction) (lua_State *L);
/*
这个类型定义了指向某个函数的指针,对应上文提到的:
必须把 C 函数的地址以一个适当的方式传递给 Lua 解释器。
*/
如何注册函数
最底层方法
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
第一行将类型为 function 的值入栈,第二行将 function 赋值给全局变量 mysin。