转自lua程序设计第二版
Lua和C通信的主要方法时一个无所不在的虚拟栈。几乎所有的API调用都会操作这个栈上的值。所有的数据交换,无论是Lua到C还是C到Lua都通过这个栈来完成。
还可以通过这个栈来保存一些中间结果。以及解决Lua和C语言中间的两大差异:
- Lua使用垃圾收集,而C要求显示地释放内存
- Lua使用动态类型,而C使用静态类型
重入
Lua没有定义任何全局变量,它将所有的状态都保存在动态结构lua_State中,所有的C API都要求传入一个指向该结构的指针。这种实现使得Lua可以重入(如果 C 程序在一个脚本上调用解释器,该脚本调用一个 C 函数,而这个 C 函数又再次使用 Lua 解释器,这是允许的。)。
关键字
luaL_State 创建一个新环境,新环境中没有包含预定义的函数
luaL_close(lua_State* l)
LuaL_openlibs(lua_State* l)打开所有的标准库
LuaL_loadbuffer编译用户输入的每行内容。如果没有错误,返回0.并向栈中压入编译后的程序块。
lua_pcall将程序块从栈中弹出,并在保护模式中运行。如果发生错误就会向栈中压入一条错误消息。用lua_tostring可以获取这条消息,打印后用lua_pop从栈中删除。
压入元素
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);
向栈中压入元素时,应该确保栈中有足够的空间。当Lua启动时,或Lua调用C语言时,栈中至少有20个空闲的槽。
//检查栈中是否有足够的空间
int lua_checkstack(lua_State * L,int sz)
查询元素
API中使用”索引”来引用栈中的元素。
第一个压入栈的为1,第二个为2,依次类推到栈顶。
使用负数来访问栈中的元素,-1表示栈顶元素,-2表示栈顶下面的元素。
检查元素是否为特定的类型
//例如lua_isnumber lua_isstring ...
int lua_is*(luaState* L,int index)
或者通过lua_type来返回栈中元素的类型。
从栈中获取元素
int lua_toboolean (lua_State *L, int index)
lua_CFunction lua_tocfunction (lua_State *L, int index);
const char *lua_tolstring (lua_State *L, int index, size_t *len);
lua_Number lua_tonumber (lua_State *L, int index);
const void *lua_topointer (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
lua_State *lua_tothread (lua_State *L, int index);
void *lua_touserdata (lua_State *L, int index);
不要在C函数之外使用在C函数内获得的指向Lua字符串的指针
lua_tolstring函数会返回一个指向内部字符串副本的指针,并将字符串的长度存入最后一个参数len中。这个内部副本不能修改,返回类型的const也说明了这点。Lua保证只要这个对应的字符串值还在栈中,那么这个指针就是有效的。当Lua调用的一个C函数返回时,Lua就会清空它的栈。
table操作
Get
BLUE = {r = 0,g = 0,b = 1}
#defien MAX_COLOR 255
//假设table位于栈顶
int getField(Lua_State *L,const char* key){
int result;
lua_pushstring(L,key);
//获取background[key]
lua_gettable(L,-2);
if(!lua_isnumber(L,-2)){
error(L,"invalid component in BLUE color");
}
result = (int)lua_tonumber(L,-1) *MAX_COLOR;
//删除数字
lua_pop(L,1);
return result;
}
lua_getglobal(L,"BLUE")
if(!lua_istable(L,-1)){
error(L,"'BLUE' is not a table");
}
red = getfield(L,"r");
green = getfield(L."g");
blue = getfield(L,"b");
Set
lua_pushstring(L,key);
lua_pushnumber(L,value);
//假设在调用钱table在栈顶(索引为-1),当压入key和value后,table就位于索引-3
lua_settable(L,-3);
创建新的table
lua_newtable(L);
...
lua_setglobal(L,name)
C 调用Lua函数
基本调用
function f(x,y)
return x + y
end
lua_getglobal(L,"f");
lua_pushnumber(L,x);
lua_pushnumber(L,y);
//完成调用(2个参数,1个结果,0表示错误处理函数索引)
if(lua_pcall(L,2,1,0) != 0){
error(L,"error running funciton f:%s",lua_tostring(L,-1))
}
//检查结果
if(!lua_isnumber(L,-1)){
error(L,"function 'f' must return a number");
}
z = lua_tonumber(L,-1);
//弹出返回值
lua_pop(L,1);
可变参数调用
void call_va(const char* func,const char* sig,...){
va_list vl;
//参数和结果的数量
int narg,nres;
va_start(vl,sig);
//压入函数
//遍历所有参数
for(narg = 0;*sig,narg++){
//检查栈中空间
luaL_checkstack(L,1,"too many arguments");
switch(*sig++){
case 'd':
lua_pushnumber(L,va_arg(vl,double));
break;
case 'i':
lua_pushnumber(L,va_arg(vl,int));
break;
...
}
}
lua_getglobal(L,func);
//压入参数
//期望的结果数量
nres = strlen(sig);
//完成调用
if(lua_pcall(L,narg,nres,0) != 0){
error(L,"error calling '%s':%s",func,lua_tostring(L,-1));
}
//检索结果
//第一个结果的栈索引
nres = -nres;
//遍历所有结果
while(*sig++){
switch(*sig++){
case 'd':
if(!lua_isnuber(L,nres)){
error(L,"wrong result type");
}
*va_arg(vl,double *) = lua_tonumber(L,nres);
break;
...
}
}
va_end(vl);
}
Lua调用C
示例
//注册函数模型 返回一个整数,表示其压入栈中的返回值数量
typedef int (*lua_CFunction)(lua_State *L);
//正弦函数
static int l_sin(lua_State *L){
//获取参数
double d = lua_tonumber(L,1);
//压入结果
lua_pushnumber(L,sin(d));
//结果的数量
return 1;
}
//压入一个函数类型的值
lua_pushcfunction(L,l_sin);
//将这个值赋予全局变量mysin
//lua调用C函数时,并不依赖函数名,包的位置或可见性规则,而值依赖于注册时传入的函数地址。
lua_setglobal(L,"mysin");
//重新编译程序后即可在lua中调用mysin函数
C模块
C模块中只有一个公共函数,用于创建C模块。而其他所有函数都是私有的,在C语言中声明为static。
//所有要注册的函数
static const struct luaL_Reg mylib[] = {
{"dir",l_dir},
//结尾
{NULL,NULL}
}
int luaopen_mylib(lua_State *L){
luaL_register(L,"mylib",mylib);
return 1;
}
require("mylib")