lua与c之间交互详解(一)
lua与c之间交互是通过“lua堆栈”通信的。不管是lua调用c还是c调用lua,都是通过操作lua堆栈实现的。顾名思义,lua堆栈也满足后进先出的特点,入栈/出栈都围绕栈顶进行的。与通用的栈不同的是,这个虚拟栈每个位置都对应一个索引,可以通过索引操作指定位置的数据。1代表栈底,向栈顶依次递增;-1代表栈顶,向栈底依次递减,如图。
1. lua中类型在c中如何表示
要实现c和lua之间的交互,先了解下lua中基本类型与c中类型怎么对应的。lua中有八种基本类型:nil、boolean、number、string、table、function、userdata、thread,其中,userdata分轻量用户数据(lightuserdata)和完成用户数据(userdata)两种。这些类型都可以压入栈中,在c中统一用TValue结构表示,是一个{值,类型}结构。
(图片来自http://www.cnblogs.com/sevenyuan/p/4511808.html)
TValue->tt表示类型,类型定义在lua.h,nil为LUA_TNIL,boolean为LUA_TBOOLEAN等
// lua.h #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8
TValue->Value是个union:
int b:只存boolean类型,注:number类型并不存在这里,b只存boolean
lua_Number n:存放所有number类型
void *p:存放轻量用户数据类型(lightuserdata)
gcObject *gc:存放所有需要垃圾回收的类型,是一个指向union GCObject的指针,通过GCObject可以看到其包含string、userdata、closure、table、proto、upvalue、thread
由此可知,nil、boolean、number、lightuserdata类型是把数据本身直接存在栈里,和lua的垃圾回收无关;而GCObject表示的类型是把数据的内存地址(即指针)存在栈里的,当生命周期结束需要垃圾回收释放内存。
2. 对堆栈的基本操作
luaL_newstate:创建一个状态机,
lua_close:关闭状态机
#include <stdio.h> #include <lua.h> #include <lauxlib.h> #include <lualib.h> int main(int argc, char *argv[]){ lua_State *L = luaL_newstate(); //创建一个状态机 lua_pushnil(L); //nil int type = lua_type(L, -1); printf("nil type = %d\n", type); if(lua_isnil(L, -1)){ printf("------nil-----\n"); } lua_pushboolean(L, 0); //boolean type = lua_type(L, -1); printf("boolean type = %d\n", type); if(lua_isboolean(L, -1)) printf("--------boolean------\n"); lua_pushlightuserdata(L, NULL); //lightuserdata type = lua_type(L, -1); printf("lightuserdata type = %d\n", type); if(lua_islightuserdata(L, -1)) printf("--------lightuserdata------\n"); lua_pushnumber(L, 10); //number type = lua_type(L, -1); printf("number type = %d\n", type); if(lua_isnumber(L, -1)) printf("--------number------\n"); lua_pushstring(L, "string"); //string type = lua_type(L, -1); printf("string type = %d\n", type); if(lua_isstring(L, -1)) printf("--------string------\n"); lua_newtable(L); //table, 创建空表,并压入栈 type = lua_type(L, -1); printf("table type = %d\n", type); if(lua_istable(L, -1)) printf("--------table------\n"); lua_newuserdata(L, 1024); //userdata, 分配1024大小的内存块,并把内存地址压入栈 type = lua_type(L, -1); printf("userdata type = %d\n", type); if(lua_isuserdata(L, -1)) printf("--------userdata------\n"); lua_pushthread(L); //thread, 创建一个lua新线程,并将其压入栈。lua线程不是OS线程 type = lua_type(L, -1); printf("thread type = %d\n", type); if(lua_isthread(L, -1)) printf("--------thread------\n"); lua_close(L); //关闭状态机 return 0; }
lua_pushXXX:push*族api向栈顶压入数据,比如lua_pushnumber压入数值,lua_pushstring压入字符串,lua_pushcclosure压入c闭包。
lua_isXXX:is*族api判断栈里指定位置的索引是否是指定类型,比如,lua_istable(L,-1)判断栈顶位置的数据是否是表,lua_isuserdata(L,-1)判断栈顶位置的数据是否是用户数据等。
gcc -o main.o main.c /usr/local/lib/liblua.a -I/usr/local/include/ -lm -ldl
运行结果如下,对应lua.h中的类型定义。c与lua之间详细的api介绍参照http://cloudwu.github.io/lua53doc/contents.html#contents