第27章 C语言API总览
lua.h(前缀 lua_): 声明了 Lua 提供的基础函数,其中包括创建新 Lua 环境的函数、调用 Lua 函数的函数等等。
#include <stdio.h> #include <string.h> #include "lua.h" #include "lauxlib.h" #include "lualib.h" int main (void) { lua_State *L = luaL_newstate(); //打开 Lua luaL_openlibs(L); //打开所有的标准库 char buff[256]; while (fgets(buff, sizeof(buff), stdin) != NULL) { //1. 编译用户输入的每一行内容,并向栈中压入编译得到的函数 //2. 从栈中弹出编译后得到的函数,并以保护模式运行 int error = luaL_loadstring(L, buff) || lua_pcall(L, 0, 0, 0); //3. 错误信息处理:获取栈中的错误信息并打印,随后删除栈中的错误信息 if (error) { fprintf(stderr, "%s ", lua_tostring(L, -1)); lua_pop(L, 1); } } lua_close(L); return 0; }
//C API压栈函数: void lua_pushnil (lua_State *L); void lua_pushboolean (lua_State *L, int bool); void lua_pushnumber (lua_State *L, lua_Number n); //默认double void lua_pushinteger (lua_State *L, lua_Integer n); //默认long long void lua_pushlstring (lua_State *L, const char *s, size_t len);//任意二进制数据,Lua会生成一个内部副本或复用已有字符串 void lua_pushstring (lua_State *L, const char *s); //' '结尾的字符串,Lua会生成一个内部副本或复用已有字符串 // minimum Lua stack available to a C function // #define LUA_MINSTACK 20 // 检查找中是否有足够的空间 int lua_checkstack(lua_State *L, int sz); void lual_checkstack(lua_State *L, int sz, const char* msg);
//C API 查询元素 //栈顶 [3] [-1] // [2] [-2] //栈底 [1] [-3] //将栈顶的值返回 int lua_toboolean (lua_State *L, int idx); const char *lua_tolstring (lua_State *L, int idx, size_t *len); int lua_tothread (lua_State *L, int idx); int lua_tonumber (lua_State *L, int idx); int lua_tointeger (lua_State *L, int idx); //检查找中的一个元素是否为特定的类型. //函数 lua_isnumber 则是检查该值是否能被转换为此特定类型 int lua_is*(lua_State *L, int idx); //*: nil, number, string, table
lua_pushvalue(L, -4); // push the value of the index to the stack lua_replace(L, 3); // pop a value and replace the index's lua_settop(L, 6); // set the top index, fill 'nil' lua_rotate (L, idx, -1);//将idx处元素移动到-1位置 #define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) lua_insert(L, -2); // move top element to the bottom LUA_POP(L, 1);
第28章 扩展应用
读取lua配置文件
--color.lua BLUE = {r=0.1, g=0.1, b=1} RED = {r=1, g=0.1, b=0} background = BLUE background = RED
/* assume that table is on the stack top */ int getfield(lua_State *L, const char *key) { int result; lua_pushstring(L, key); lua_gettable(L, -2); /* push background[key] and remove key*/ if (!lua_isnumber(L, -1)) error(L, "invalid component in background color"); result = lua_tonumber(L, -1) * MAX_COLOR; lua_pop(L, 1); /* remove number */ return result; } void loadTable() { char filename[] = "c_call_lua/color.lua"; lua_State *L = luaL_newstate(); luaL_openlibs(L); // This is necessary for os.genenv if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) { error(L, "cannot run configuration file: %s", lua_tostring(L, -1)); } lua_getglobal(L, "background"); if (!lua_istable(L, -1)) error(L, "`background' is not a valid color table"); int red = getfield(L, "r"); int green = getfield(L, "g"); int blue = getfield(L, "b"); printf ("r = %d, g = %d, b = %d ", red, green, blue); lua_close(L); }
void error (lua_State *L, const char *fmt, ...) { va_list vl; va_start(vl, fmt); vfprintf(stderr, fmt, vl); va_end(vl); lua_close(L); exit(EXIT_FAILURE); printf ("won't print "); } void callLuaf(lua_State *L, const char *func, const char *sig, ...) { lua_getglobal(L, func); //if (lua_getglobal(L, func) != 0) // error(L, "error getting function `%s': %s", func, lua_tostring(L, -1)); int narg = 0;/* number of arguments */ va_list vl; va_start(vl, sig); while (*sig) { /* push arguments */ switch (*sig++) { case 'd': {/* double argument */ double d = va_arg(vl, double); lua_pushnumber(L, d); break; } case 'i': {/* int argument */ int i = va_arg(vl, int); lua_pushnumber(L, i); break; } case 's': {/* string argument */ char* c = va_arg(vl, char*); lua_pushstring(L, c); break; } case '>': goto endwhile; default: error(L, "invalid option (%c)", *(sig - 1)); } narg++; luaL_checkstack(L, 1, "too many arguments"); } endwhile: /* do the call */ int nres = strlen(sig); /* number of results */ /* 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); } int callwk(const char *filename, const char* func) { lua_State *L = luaL_newstate(); luaopen_base(L); luaopen_table(L); luaopen_package(L); luaopen_io(L); luaopen_string(L); luaopen_math(L); luaL_openlibs(L); // open all the libs above if (luaL_dofile(L, filename)) error(L, "luaL_dofile: %s ", lua_tostring(L, -1)); callLuaf(L, func, ""); lua_close(L); //double x = 3.0; //double y = 2.0; //double z = 0; //callLuaf(L, func, "dd>d", x, y, &z);//call func(L, x, y); //printf ("In %s, %s(%f, %f) = %f ", filename, func, x, y, z); return 0; }
第29章 在Lua中调用C语言
package.cpath = '/home/yoyu/hub/testcode/lua/api/?.so;' --搜索so模块 local lib = require "libtest" print("call c lib: lib.lsum(23,17) = "..lib.lsum(23,17)) local files = lib.ldir(".") -- Get file list in current dir
//libtest.c int sum(lua_State *L){ double d1 = luaL_checknumber(L, 1); double d2 = luaL_checknumber(L, 2); lua_pushnumber(L, d1+d2); return 1; } int dir (lua_State *L) { DIR *dir; struct dirent *entry; int i; const char *path = luaL_checkstring(L, 1); /* open directory */ dir = opendir(path); if (dir == NULL) { lua_pushnil(L); /* error opening the directory? */ /* return nil and ... */ lua_pushstring(L, strerror(errno)); /* error message */ return 2; /* number of results */ } /* create result table */ lua_newtable(L); i = 1; while ((entry = readdir(dir)) != NULL) { lua_pushnumber(L, i++); /* push key */ lua_pushstring(L, entry->d_name); /* push value */ //printf("entry->d_name : %s ", entry->d_name); lua_settable(L, -3); } closedir(dir); return 1; /* table is already on top */ } //Name of array mylib can be changed. static const struct luaL_Reg mylib[] = { {"lsum", sum}, {"ldir", dir}, {NULL, NULL} }; //Function name can not be changed. int luaopen_libtest(lua_State *L){ luaL_newlib(L, mylib); return 1; }
#Makefile CFLAG1=-g -Wall -llua -shared -fPIC #build lib CFLAG2=-g -Wall -llua -lm -ldl -ltest -fPIC #use lib in c
Lua 语言中的每个协程都有自己的软栈( soft stack ), 对于 C 函数的调用 ,解释器必须使用 C 语言栈。因此, Lua 中的协程不能挂起 C 函数的执行:如果一个 C 函数位于从 resume 到对应yield 的调用路径中,那么 Lua 无法保存 C 函数的状态以便于在下次 resume 时恢复状态。因此,Lua 5.1 不能将其挂起。
--test.lua:pcall 是一个 C 语言函数 co = coroutine.wrap(function() print(pcall(coroutine.yield)) end) co() --~/hub/lua/lua-5.1.4/src/lua testlua/test.lua ---> false attempt to yield across metamethod/C-call boundary --~/hub/lua/lua-5.3.6/src/lua testlua/test.lua ---> OK
第30章 编写C函数的技巧
//test.lua local s = "hi,,there" print("will split:"..s) local split_result = lib.lsplit(s, "i") printAll(split_result, "split_result", 2, "hh ")
//libtest.c int split (lua_State *L) { const char *s = luaL_checkstring(L, 1); const char *sep = luaL_checkstring(L, 2); const char *e; int i = 1; lua_newtable(L); /* result */ /* repeat for each separator */ while ((e = strchr(s, *sep)) != NULL) { lua_pushlstring(L, s, e - s); /* push substring */ lua_rawseti(L, -2, i++); /* insert to table */ s = e + 1; /* skip separator */ } /* push last substring */ lua_pushstring(L, s); lua_rawseti(L, -2, i); return 1; /* return the table */ }
lua_concat(L, n) 会连接(并弹出)栈最顶端的 n 个值,并将结果压人栈。
LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);
如果不知道返回结果大小的上限值,使用下面的函数:
LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B); LUALIB_API void luaL_addvalue (luaL_Buffer *B); LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l); LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s); #define luaL_addchar(B,c) ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), (*(B)->p++ = (char)(c))) LUALIB_API void luaL_pushresult (luaL_Buffer *B);
示例:函数 table.concat 一个简化的实现
static int tconcat(lua_State *L) { luaL_Buffer b; int i, n; luaL_checktype(L, 1, LUA_TTABLE); n = luaL_len(L, 1); luaL_buffinit(L, &b); for (i = 1; i <= n; i++) { lua_geti(L, 1, i); // Get string from table luaL_addvalue(&b); // Put string in buff } luaL_pushresult(&b); char* s = lua_tostring(L, -1); printf("in c : s = %s ", s); //lua_remove(L, -1); return 1; }
注册表是一张只能被 C 代码访问的全局表 。 通常情况下,我们使用注册表来存储多个模块间共享的数据 。
static int set_regref(lua_State *L) { int key1 = luaL_ref(L, LUA_REGISTRYINDEX); char data_of_key1[] = "char data of key1"; lua_pushlightuserdata(L, (void *)&key1); /* push address */ lua_pushstring(L, data_of_key1); /* push value */ lua_settable(L, LUA_REGISTRYINDEX); /* registry[&key1] = data_of_key1*/ printf("key1 = %d set registry[&%p] = %s ", key1, &key1, data_of_key1); int key2 = luaL_ref(L, LUA_REGISTRYINDEX); int number_of_key2 = key2 * 1000; lua_pushlightuserdata(L, (void *)&key2); /* push address */ lua_pushnumber(L, number_of_key2); /* push value */ lua_settable(L, LUA_REGISTRYINDEX); /* registry[&key2] = number_of_key2 */ printf("key2 = %d set registry[&%p] = %d ", key2, &key2, number_of_key2); lua_rawgeti(L, LUA_REGISTRYINDEX, key1); /* why needed? */ lua_rawgeti(L, LUA_REGISTRYINDEX, key2); /* why needed? */ lua_pushnumber(L, (long)(&key1)); /* push address */ lua_pushnumber(L, (long)(&key2)); /* push address */ printf("key1 = %p ", (void *)lua_tointeger(L, -2)); printf("key2 = %p ", (void *)lua_tointeger(L, -1)); return 2; /* return the keys */ }
int get_regref(lua_State *L) { long key1_addr = luaL_checknumber(L, 1); long key2_addr = luaL_checknumber(L, 2); /* retrieve a string*/ lua_pushlightuserdata(L, (void*)key1_addr); /* push address. */ lua_gettable(L, LUA_REGISTRYINDEX); /* retrieve value */ const char *key1_data = lua_tostring(L, -1); /* convert to string*/ printf("get registry[&%p] = %s ", (void*)key1_addr, key1_data); /* retrieve a number */ lua_pushlightuserdata(L, (void*)key2_addr); /* push address */ lua_gettable(L, LUA_REGISTRYINDEX); /* retrieve value */ int key2_data = lua_tonumber(L, -1); /* convert to number */ printf("get registry[&%p] = %d ", (void*)key2_addr, key2_data); luaL_unref(L, LUA_REGISTRYINDEX, (int)key1_addr); luaL_unref(L, LUA_REGISTRYINDEX, (int)key2_addr); printf("return "); return 0; /* return the table */ }
function test_reg() local key1, key2 = lib.lset_regref() print(string.format("%x", key1)) print(string.format("%x", key2)) lib.lget_regref(key1, key2) end
上值( upvalue ) 实现了 一种类似于 C 语言静态变量 (只在特定的函数中可见)的机制。C 函数与其上值 的关联称为闭包 ( closure )。
static int counter(lua_State *L) { int val = lua_tointeger(L, lua_upvalueindex(1)); lua_pushinteger(L, ++val); //新值 lua_copy(L, -1, lua_upvalueindex(1)); //用新值更新upvalue return 1; //返回新值 } int newCounter(lua_State *L) { lua_pushinteger(L, 1); //压入upvalue,初始值为1 lua_pushcclosure(L, &counter, 1); //创建闭包 return 1; //返回闭包 }
function test_counter() local c1 = lib.lnewCounter() local c2 = lib.lnewCounter() print(c1(), c2(), c1(), c2(), c1(), c2()) end
元组是一种具有匿名字段的常量结构,我们可以用一个数值索引来获取某个特定的字段,或者一次性地获取所
有字段 。 在我们的实现中,将元组表示为函数,元组的值存储在函数 的上值中 。
int t_tupple(lua_State *L) { lua_Integer op = luaL_optinteger(L, 1, 0); //获取1个参数值,默认0 if (op == 0) { //没有参数 int i; /*将每一个有效的upvalue压栈*/ for (i = 1; !lua_isnone(L, lua_upvalueindex(i)); ++i) { lua_pushvalue(L, lua_upvalueindex(i)); } return i - 1; //值的个数 } else { luaL_argcheck(L, 0 < op && op <= 256, 1, "index out of range"); if (lua_isnone(L, lua_upvalueindex(op))) { return 0; //字段不存在 } lua_pushvalue(L, lua_upvalueindex(op)); return 1; } } int t_new(lua_State *L) { int top = lua_gettop(L); luaL_argcheck(L, top < 256, top, "too many fields"); lua_pushcclosure(L, t_tupple, top); return 1; }
function tupple_test() x = lib.lnew(10, "hi tupple", {}, 3) print(x(1)) print(x(2)) print(x()) print(x(256)) end
第31章 C语言中的用户自定义类型
用户数据(userdata),元表,数组访问
#include "libtest.h" int newarray (lua_State *L) { int n = luaL_checkinteger(L, 1); size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double); //把这块分配的内存压入栈,并把地址存入 a。 NumArray *a = (NumArray *)lua_newuserdata(L, nbytes); a->size = n; luaL_getmetatable(L, "LuaBook.array"); lua_setmetatable(L, -2); return 1; // new userdatum is already on the stack } //判断第一个参数的元表是否为 registry["LuaBook.array"] #define checkarray(L) (NumArray *)luaL_checkudata(L, 1, "LuaBook.array") static int getindex (lua_State *L) { NumArray *a = checkarray(L); int index = luaL_checkinteger(L, 2) - 1; luaL_argcheck(L, 0 <= index && index < a->size, 2,"index out of range"); return index; /* return element address */ } int getsize (lua_State *L) { NumArray *a = checkarray(L); lua_pushnumber(L, a->size); return 1; } static double *getelem (lua_State *L) { NumArray *a = checkarray(L); int index = getindex(L); return &a->values[index]; // return element address. } int setarray (lua_State *L) { double newvalue = luaL_checknumber(L, 3); *getelem(L) = newvalue; return 0; } int getarray (lua_State *L) { double ret = *getelem(L); lua_pushnumber(L, ret); return 1; } int array2string (lua_State *L) { NumArray *a = checkarray(L); lua_pushfstring(L, "array(%d)", a->size); return 1; } //Name of array mylib can be changed. static const struct luaL_Reg thelib[] = { {"new", newarray}, {"set", setarray}, {"get", getarray}, {"size", getsize}, {NULL, NULL} }; static const struct luaL_Reg arraylib_m[] = { {"__newindex", setarray}, {"__index", getarray}, {"__len", getsize}, {"__tostring", array2string}, {NULL,NULL} }; //Function name can not be changed. int luaopen_libtest(lua_State *L){ luaL_newmetatable(L, "LuaBook.array"); // 创建元表 mt luaL_setfuncs(L, arraylib_m, 0); luaL_newlib(L, thelib); return 1; }
asize = 1000 a = lib.new(asize) print(#a) for i = 1, asize do a[i] = 1 / i --a.set(a, i, 1/i) -- or a:set(i, 1/i) end print(a[10])
轻量用户数据( light userdata )代表指针,用其代替 userdata 会使开销少很多,内存必须自己管理,因为它不会被垃圾回收。
#include "libtest.h" static int dir_iter(lua_State *L); static int dir_gc(lua_State *L) { DIR *dir = *(DIR**)lua_touserdata(L, 1); if (dir) { closedir(dir); } return 0; } int dirV2 (lua_State *L) { const char *path = luaL_checkstring(L, 1); DIR **dir = (DIR**)lua_newuserdata(L, sizeof(DIR*)); *dir = NULL; // 预先初始化 luaL_getmetatable(L, "LuaBook.dir"); lua_setmetatable(L, -2); *dir = opendir(path); //创建并返回迭代函数;该函数唯一的upvalue,即代表目录的用户数据位于栈顶 lua_pushcclosure(L, dir_iter, 1); return 1; } static int dir_iter(lua_State *L) { DIR *dir = *(DIR**)lua_touserdata(L, lua_upvalueindex(1)); struct dirent *entry = readdir(dir); if (entry) { lua_pushstring(L, entry->d_name); return 1; } else { return 0; //遍历完成 } } //Name of array mylib can be changed. static const struct luaL_Reg thelib[] = { {"ldir", dirV2},//dir}, {NULL, NULL} }; //Function name can not be changed. int luaopen_libtest(lua_State *L){ luaL_newmetatable(L, "LuaBook.dir"); lua_pushcfunction(L, dir_gc); lua_setfield(L, -2, "__gc"); luaL_newlib(L, thelib); return 1; }
for fname in lib.ldir(".") do print(fname) end