0915学习 agile Posted on Sep 15 2023 面试 lua ##lua与C --- ###Lua的虚拟栈 --- - 每一个与Lua通信的C函数都有其独有的虚拟栈,虚拟栈由Lua管理 - 栈中的数据通过索引值进行定位 - 其中栈顶是-1,栈底是1,也就是第1个入栈的在栈底 - 正数表示相对于栈底的位置(位移),负数表示相对于栈顶的位置(位移) --- --- ###方法调用 ```C //nargs 参数个数 //nresults 返回值个数 void lua_call (lua_State *L, int nargs, int nresults); // a = f("how", t.x, 14) lua_getglobal(L, "f"); /* function to be called */ lua_pushliteral(L, "how"); /* 1st argument */ lua_getglobal(L, "t"); /* table to be indexed */ lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */ lua_remove(L, -2); /* remove 't' from the stack */ lua_pushinteger(L, 14); /* 3rd argument */ lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */ lua_setglobal(L, "a"); /* set global 'a' */ ``` --- ###Lua调用C函数的两种方式 --- - 程序主体在C中运行,C函数注册到Lua中。C调用Lua,Lua调用C注册的函数,C得到函数的执行结果 --- ####Main.cpp --- ```C #include <iostream> #include "math.h" extern "C" { #include "lauxlib.h" #include "lualib.h" #include "lua.h" } void loadbuffer(); /* 所有注册给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; } /* 需要一个"luaL_Reg"类型的结构体,其中每一个元素对应一个提供给Lua的函数。 * 每一个元素中包含此函数在Lua中的名字,以及该函数在C库中的函数指针。 * 最后一个元素为“哨兵元素”(两个"NULL"),用于告诉Lua没有其他的函数需要注册。 */ static const luaL_Reg luaReg[] = { {"l_sin", l_sin}, {NULL, NULL} }; LUA_API void luaopen_mylib(lua_State *L) { //两种方式实现 // luaL_openlibs(L); // 利用函数luaL_newlib将C语言函数以库的方式加载到Lua环境中 // luaL_newlib(L, luaReg); // lua_setglobal(L, "Temp"); // 将C函数转换为Lua的"function"并压入虚拟栈。 lua_pushcfunction(L, l_sin); // 弹出栈顶元素,并在Lua中用名为"mysin"的全局变量存储。 lua_setglobal(L, "l_sin"); } int main() { loadbuffer(); return 0; } void loadbuffer() { char buff[256] = {0}; int error; lua_State *l = luaL_newstate(); luaL_openlibs(l); luaopen_mylib(l); while (fgets(buff, sizeof(buff), stdin) != nullptr) { error = luaL_loadbuffer(l, buff, strlen(buff), "chunk") || lua_pcall(l, 0, 0, 0); if (error) { fprintf(stderr, "%s", lua_tostring(l, -1)); lua_pop(l, 1); } } lua_close(l); } ``` --- ####CMakeLists.txt ```txt cmake_minimum_required(VERSION 3.22) project(luatest) if (NOT LUA_VERSION) set(LUA_VERSION "5.4.6") endif () set(CMAKE_CXX_STANDARD 11) set(LUA_SRC_PATH lua-${LUA_VERSION}/src) aux_source_directory(${LUA_SRC_PATH} LUA_CORE) list(REMOVE_ITEM LUA_CORE ${LUA_SRC_PATH}/lua.c ${LUA_SRC_PATH}/luac.c) add_library(lua STATIC ${LUA_CORE}) include_directories(./lua-${LUA_VERSION}/src) add_executable(luatest main.cpp) target_link_libraries(luatest lua) ``` --- - 程序主体在Lua中运行,C函数作为库函数供Lua使用 --- ####library.h --- ```C #ifndef LUALIB_LIBRARY_H #define LUALIB_LIBRARY_H #ifdef __cplusplus extern "C" { #endif #include "lualib.h" #include "lua.h" #include "lauxlib.h" LUA_API int luaopen_libmyLib(lua_State *L); #endif //LUALIB_LIBRARY_H #ifdef __cplusplus } #endif ``` --- ####library.cpp --- ```C #include "library.h" #include "math.h" static int testFunc(lua_State *L) { printf("http://www.jellthink.com\n"); lua_pushnumber(L, 10); return 1; } static int l_Sin(lua_State *L) { double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin(d)); return 1; } static const struct luaL_Reg myLib[] = { {"test", testFunc}, {"mySin", l_Sin}, {NULL, NULL} }; /* 此函数为C库中的“特殊函数”。 * 通过调用它注册所有C库中的函数,并将它们存储在适当的位置。 * 此函数的命名规则应遵循: * 1、使用"luaopen_"作为前缀。 * 2、前缀之后的名字将作为"require"的参数。 */ LUA_API int luaopen_libmyLib(lua_State *L) { /* void luaL_newlib (lua_State *L, const luaL_Reg l[]); * 创建一个新的"table",并将"l"中所列出的函数注册为"table"的域。 */ luaL_newlib(L, myLib); lua_setglobal(L, "myLib"); return 1; // 把表压入了栈中,所以就需要返回1 } ``` --- ####CMakeLists.txt --- ```txt cmake_minimum_required(VERSION 3.22) project(lualib) set(CMAKE_CXX_STANDARD 11) include_directories(/usr/local/include/lua) link_directories(/usr/local/lib) #不能用static,lua require识别不了静态库 add_library(myLib MODULE library.cpp) target_link_libraries(myLib lua) ``` --- ###test.lua --- ```lua --[[ 这里"require"的参数对应C库中"luaopen_libmyLib()"中的"libmyLib"。 C库就放在"test.lua"的同级目录,"require"可以找到。]] require "libmyLib" -- 结果与上面的例子中相同,但是这里是通过调用C库中的函数实现。 print(myLib.mySin(3.14 / 2)) --> 0.99999968293183 ``` --- - `typedef int (*lua_CFunction) (lua_State *L); `:Lua中注册的C函数必须有同样的原型。 - 被注册的C函数接收一个单一的lua_State类型的参数,同时返回一个表示返回值个数的数字。 - 函数在将返回值入栈之前无需清理栈,在函数返回之后,Lua会自动清除栈中返回结果下面的所有内 --- ####stackDump --- ```C void InsertData(lua_State *l) { lua_pushnumber(l, 10); lua_pushnil(l); lua_pushstring(l, "hello world!"); lua_pushboolean(l, 1); // 10 nil hello world! true StackDump(l); // 将索引-2处的值的副本入栈 lua_pushvalue(l, -2); // 10 nil hello world! true hello world! StackDump(l); // 将栈顶元素移动到索引2处,并覆盖原先的元素。 lua_replace(l, 2); // 10 hello world! hello world! true StackDump(l); // 移除索引-3处的元素,其上所有元素下移。 lua_remove(l, -3); // 10 hello world! true StackDump(l); // 将栈顶设置为索引-3处 lua_settop(l, -3); // 10 StackDump(l); } void StackDump(lua_State *L) { int n = lua_gettop(L); for (int i = 1; i <= n; ++i) { int t = lua_type(L, i); switch (t) { case LUA_TSTRING: printf("%s", lua_tostring(L, i)); break; case LUA_TNIL: printf("nil"); break; case LUA_TNUMBER: printf("%g", lua_tonumber(L, i)); break; case LUA_TBOOLEAN: printf(lua_toboolean(L, i) ? "true" : "false"); break; default: printf("type:%s", lua_typename(L, i)); break; } printf(" "); } printf("\n"); } ``` --- ####调用方法 --- - 将Lua函数入栈。 - 将参数按照形参的顺序依次入栈。 - 调用函数。调用完成后,会将函数的参数以及Lua函数出栈,并将函数的返回值入栈。 - 获取函数的返回值。 --- ```C static int l_Map(lua_State *L) { // 因为具有独立的虚拟栈,所以检查"index"为1的位置是否是"table"。 luaL_checktype(L, 1, LUA_TTABLE); // 因为具有独立的虚拟栈,所以检查"index"为2的位置是否是函数。 luaL_checktype(L, 2, LUA_TFUNCTION); /* 获取"index"为1的位置的元素("table")的长度。 * 对于"table",它在不触发"metamethod"(__index)的情况下, * 获取"table"中元素的个数。 */ int n = lua_rawlen(L, 1); for (int i = 1; i <= n; ++i) { // 将Lua函数(f)入栈。 lua_pushvalue(L, 2); // 将Lua函数的参数(t[i])入栈。 lua_rawgeti(L, 1, i); // (以非保护模式)调用Lua函数(f(t[i]))。 lua_call(L, 1, 1); // 将函数的返回值存储到"table"中对应的位置(t[i] = result)。 lua_rawseti(L, 1, i); } return 0; } ``` --- ```lua require("libmyLib") local t = { 1, 2, 3 } local f = function(n) return n * n; end myLib.map(t, f) --1 1 --2 4 --3 9 for k, v in pairs(t) do print(k,v) end ``` --- ####string方法相关 --- ```C static int l_Split(lua_State *L) { // 检查前两个参数是否为字符串,并获得它们(待分割字符串以及分隔符)。 const char *s = luaL_checkstring(L, 1); const char *sp = luaL_checkstring(L, 2); const char *e = nullptr; int i = 0; /* 创建一个Lua的"table"并入栈(用于存储结果)。 * 由于函数的参数在虚拟栈"index"为1和2的位置, * 所以新创建的"table"将在虚拟栈"index"为3的位置。 */ lua_newtable(L); // 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置 // char *strchr(const char *str, int c) while ((e = strchr(s, *sp))) { /* 将被分割出来的字符串入栈。 * 由于字符串没有结束符('\0'), * 所以使用"lua_pushlstring()"而非"lua_pushstring()"。 */ lua_pushlstring(L, s, (e - s)); lua_rawseti(L, -2, ++i);// "t[i] = 字符串"。 s = e + 1; // 跳过分隔符。 } // 将最后一个分隔符后面的字符串入栈。 lua_pushstring(L, s); lua_rawseti(L, -2, ++i); return 1; } static int l_StrLength(lua_State *L) { size_t l; luaL_checklstring(L, 1, &l); lua_pushinteger(L, (lua_Integer) l); return 1; } static int l_StrReverse(lua_State *L) { size_t l; /* 检查传递的参数是否是字符串,"l"获得字符串的长度。 *(因为不会修改字符串,所以"l"就是结果字符串的长度) */ const char *s = luaL_checklstring(L, 1, &l); luaL_Buffer b;// 申请一个缓存。 // 为缓存预分配以及初始化一个指定大小的缓冲区。 char *p = luaL_buffinitsize(L, &b, l); for (int i = 0; i < l; ++i) { p[i] = s[l - i - 1]; } // 将缓冲区中的字符串放入缓存,结束对缓存的使用,并将结果字符串入栈。 luaL_pushresultsize(&b, l); return 1; } ``` --- ```lua local t = { "hello ", " world ", " 1 ", " 2 ", " 3 ", " !", "!", "!" } local s = myLib.concat(t) --hello world 1 2 3 !!! print(s) local r = myLib.split(s, ' ') for i, v in pairs(r) do print(i, v) end --26 26 print(myLib.len(s), string.len(s)) --!!! 3 2 1 dlrow olleh print(myLib.reverse(s)) ``` --- ###registry --- - Lua提供了一张特殊的”table”,它可以供C代码随意使用。但是对于Lua代码,访问却是被禁止的 - `pseudo-index`(假索引)类似于虚拟栈中正常的索引 - 与正常索引的区别在于,虽然使用它也是通过虚拟栈,但是其所对应的值并不是存储在虚拟栈中 - `LUA_REGISTRYINDEX`就是一个`pseudo-index` - 它用于通过虚拟栈访问`registry`(但”registry”并非实际存储在虚拟栈中) - 获取”registry”中索引为”Key”的元素的值: `lua_pushstring(L, "Key"); lua_gettable(L, LUA_REGISTRYINDEX);` --- ####保证”registry”中的索引唯一 --- - `registry`就是一个普通的Lua的`table`,因此你可以像访问其他”table”一样使用非nil的值作为`key`存取它的元素 --- ####使用`static`变量的地址作为`key` --- ```C static int l_Registry(lua_State *L) { // "key"变量存储什么值不重要,因为用到的是它本身的地址。 static const char key1 = 'k'; static const char key2 = 'k'; // 将"key"入栈。 lua_pushlightuserdata(L, (void *) &key1); // 将"value"入栈。 lua_pushnumber(L, 10); // "registry[key] = value"。 lua_settable(L, LUA_REGISTRYINDEX); // 不同的"key",操作"registry"中不同的元素。 lua_pushlightuserdata(L, (void *) &key2); lua_pushnumber(L, 11); lua_settable(L, LUA_REGISTRYINDEX); // 相同的"key",操作"registry"中相同的元素。 lua_pushlightuserdata(L, (void *) &key1); lua_pushnumber(L, 12); lua_settable(L, LUA_REGISTRYINDEX); // 将"key"入栈。 lua_pushlightuserdata(L, (void *) &key1); // "registry[key]"。 lua_gettable(L, LUA_REGISTRYINDEX); // 获得虚拟栈顶的结果。 //key1:12 printf("key1:%d\n", (int) lua_tonumber(L, -1)); lua_pushlightuserdata(L, (void *) &key2); lua_gettable(L, LUA_REGISTRYINDEX); // key2:11 printf("key2:%d\n", (int) lua_tonumber(L, -1)); return 0; } ``` --- ```lua --key1:12 --key2:11 myLib.registryTest() ``` --- ####reference system - `referencesystem`由一对儿定义在辅助库中的函数组成。使用他们,你可以无需关心如何创建唯一的`key`,便可以在`registry`中自由的存取数据 --- ```C static int l_RegistryRef(lua_State *L) { // 数据入栈。 lua_pushinteger(L, 10); // 将数据存入"registry",并得到数据所对应的"reference"。 int ref = luaL_ref(L, LUA_REGISTRYINDEX); lua_pushinteger(L, 20); // 重新设置值 lua_rawseti(L, LUA_REGISTRYINDEX, ref); // 从"registry"中获取"reference"所对应的数据,并将其入栈。 lua_rawgeti(L, LUA_REGISTRYINDEX, ref); // 20 printf("value:%d\n", (int) lua_tointeger(L, -1)); luaL_unref(L, LUA_REGISTRYINDEX, ref); return 0; } ``` --- ###C库函数中实现`Closure` --- ```C static int fooClosure(lua_State *L) { int number = lua_tointeger(L, lua_upvalueindex(1)); lua_pushinteger(L, ++number); lua_pushvalue(L, -1); // 将栈顶元素移动到索引"index"处。(相当于覆盖了索引"index"处的元素) //更新upvalue lua_replace(L, lua_upvalueindex(1)); return 1; } static int l_FooClosure(lua_State *L) { lua_pushinteger(L, 100); lua_pushcclosure(L, fooClosure, 1); return 1; } ``` --- ```lua local fooClosure = myLib.fooClosure() --101 print(fooClosure()) --102 print(fooClosure()) --103 print(fooClosure()) local fooClosure2 = myLib.fooClosure() --101 print(fooClosure2()) ``` --- ###userdata --- <table><thead><tr><th>\</th><th>“full userdata”</th><th>“light userdata”</th></tr></thead><tbody><tr><td>本质</td><td>一段在被创建时可以指定大小的内存区域,通常用来表示 C 中的结构体。</td><td>一小段固定的内存区域,通常用来表示 C 中的指针 (<code onclick="mdcp.copyCode(event)">void *</code>)</td></tr><tr><td>使用</td><td>需要显式的创建一块儿内存,该段内存由 Lua 的垃圾回收器管理,使用者无需关心。</td><td>无需创建内存,它就相当于一个值(就像 Lua 中的数值一样),它所使用的内存空间不由 Lua 的垃圾回收器管理,所以使用者需要关心其内存使用。</td></tr><tr><td>创建</td><td><code onclick="mdcp.copyCode(event)">void *lua_newuserdata(lua_State *L, size_t size);</code></td><td><code onclick="mdcp.copyCode(event)">void lua_pushlightuserdata(lua_State *L, void *p);</code></td></tr><tr><td>其他</td><td>可以指定其”metatable” 和”metamethods”。</td><td>不能指定其”metatable” 和”metamethods”。</td></tr></tbody></table> --- ```c /* 分配一块大小为"size"的内存空间作为"full userdata"使用, * 之后将内存空间的地址入栈,函数返回此地址。 */ void *lua_newuserdata(lua_State *L, size_t size); /* 检查"cond"是否为"true",如果为"false"则报错,并返回形如如下格式的错误, * "bad argument #arg to 'funcname' (extramsg)" */ void luaL_argcheck (lua_State *L, int cond, int arg, const char *extramsg); ``` --- ```C typedef struct NumArray { int size; // 数组声明为一个长度只是为了占位,因为C中不允许声明长度为0的数组, // 在实际程序中,我们会根据指定的大小来申请合适的空间, double values[1]; } NumArray; static int l_NewArray(lua_State *L) { int n = (int) luaL_checkinteger(L, 1); // "n"为指定的大小。因为"NumArray"中已包含了一个元素的大小,所以需要减去。 size_t s = sizeof(NumArray) + (n - 1) * sizeof(double); auto a = (NumArray *) lua_newuserdata(L, s); // auto a = (struct NumArray *) lua_newuserdata(L, s); a->size = n; return 1; } static int l_Size(lua_State *L) { auto a = (NumArray *) lua_touserdata(L, 1); luaL_argcheck(L, a != nullptr, 1, "参数不合法"); lua_pushinteger(L, a->size); return 1; } static int l_SetArray(lua_State *L) { auto a = (NumArray *) lua_touserdata(L, 1); int index = (int) lua_tointeger(L, 2); auto value = (double) lua_tonumber(L, 3); luaL_argcheck(L, a != nullptr, 1, "传的为null"); luaL_argcheck(L, index >= 1 && index <= a->size, 2, "index 序列不合法"); a->values[index - 1] = value; return 0; } static int l_GetArray(lua_State *L) { auto a = (NumArray *) lua_touserdata(L, 1); int index = (int) lua_tointeger(L, 2); luaL_argcheck(L, a != nullptr, 1, "传的为null"); luaL_argcheck(L, index >= 1 && index <= a->size, 2, "index 序列不合法"); lua_pushnumber(L, a->values[index - 1]); return 1; } ``` --- ```lua local array = myLib.newArray(10) print(myLib.arraySize(array)) for i = 1, 10 do myLib.setArray(array, i, i / 10) end print(myLib.getArray(array, 1)) ``` --- ####usedata的metatable --- ```C #include "Queue.h" #include <stdlib.h> #include <string.h> typedef struct queue { int size; int head; int tail; int *ref; } queue; #define q_malloc malloc #define q_free free #define q_realloc realloc #define q_calloc calloc const char *const QUQUE_KEY = "AGILE_QUQUE"; /* 检查虚拟栈中索引"arg"处的值是否为一个"userdata", * 并且此"userdata"具有一个名为"tname"的"metatable"。 * 如果是,则返回此"userdata"的地址,否则返回"NULL"。 */ #define checkQueue(L) (queue*)luaL_checkudata(L, 1, QUQUE_KEY) #define INIT_SIZE 8 #define allocSize(size) ((size) * sizeof(int)) static int l_new(lua_State *L) { size_t byteSize = sizeof(queue); auto q = (queue *) lua_newuserdata(L, byteSize); luaL_setmetatable(L, QUQUE_KEY); // luaL_getmetatable(L, QUQUE_KEY); // lua_setmetatable(L, -2); q->size = INIT_SIZE; q->ref = (int *) q_malloc(allocSize(q->size)); // 还可以给userdata关联一个值, // 在Lua层通过debug.setuservalue|debug.getuservalue // 在C层通过lua_setuservalue|lua_getuservalue //真正的数据就保持在这个table中 lua_newtable(L); lua_setuservalue(L, -2); q->head = 0; q->tail = 0; return 1; } inline static int get_len(queue *q) { int tail = q->tail; int head = q->head; int size = q->size; if (tail >= head) { return tail - head; } else { return size - (head - tail); } } static int l_len(lua_State *L) { auto q = checkQueue(L); lua_pushinteger(L, get_len(q)); return 1; } static void try_grow(queue *q) { int qLen = get_len(q); if (qLen + 1 >= q->size) { int newSize = q->size * 2; q->ref = (int *) q_realloc(q->ref, allocSize(newSize)); if (q->tail < q->head) { int count = q->size - q->head; int newHead = newSize - count; memmove(q->ref + newHead, q->ref + q->head, count * sizeof(int)); q->head = newHead; } q->size = newSize; } } static int l_push(lua_State *L) { auto q = checkQueue(L); luaL_checkany(L, 2); try_grow(q); lua_getuservalue(L, 1); lua_pushvalue(L, 2); // int tail = q->tail; // int head = q->head; // int size = q->size; // printf("push start len:%d\n", get_len(q)); // printf("head:%d,tail:%d,size:%d\n", q->head, q->tail, q->size); /* 生成一个唯一的"key",将栈顶的值出栈作为"value", * 从虚拟栈的索引"t"处获得"table",之后做相当于"table[key] = value"的操作。 * 函数返回生成的"key"。 * 如果"value"为"nil",则不会生成"key",同时函数返回"LUA_REFNIL"。 * 同时Lua还定义了"LUA_NOREF",它代表一个与"luaL_ref"所返回的任何值都不同的值。 */ q->ref[q->tail] = luaL_ref(L, -2); q->tail = (q->tail + 1) % q->size; // printf("push end len:%d\n", get_len(q)); return 0; } static int l_pop(lua_State *L) { auto q = checkQueue(L); // printf("pop start len:%d\n", get_len(q)); if (get_len(q) != 0) { int ref = q->ref[q->head]; q->head = (q->head + 1) % q->size; if (ref == LUA_REFNIL) { lua_pushnil(L); } else { lua_getuservalue(L, 1); //获取返回值 lua_rawgeti(L, -1, ref); /* 从虚拟栈的索引"t"处获得"table",之后做相当于"table[ref] = nil"的操作。 * "ref"同时也被释放(可以再次供"luaL_ref"使用)。 * 如果"ref"是"LUA_NOREF"或是"LUA_REFNIL",则函数不做任何操作。 */ luaL_unref(L, -2, ref); } } else { //长度为空的情况下 lua_pushnil(L); } // printf("pop end len:%d\n", get_len(q)); return 1; } static int l_index(lua_State *L) { auto q = checkQueue(L); auto index = (int) luaL_checknumber(L, 2) - 1; auto len = get_len(q); // printf("index:%d,len:%d\n", index, len); luaL_argcheck(L, index >= 0 && index < len, 2, "输入的index不在范围内!!!"); auto ref = q->ref[(q->head + index) % q->size]; if (ref == LUA_REFNIL) { lua_pushnil(L); } else { lua_getuservalue(L, 1); lua_rawgeti(L, -1, ref); } return 1; } static int l_gc(lua_State *L) { auto q = checkQueue(L); q_free(q->ref); return 0; } static int l_string(lua_State *L) { auto q = checkQueue(L); lua_pushfstring(L, "queue:%p", q); return 1; } static int l_toTable(lua_State *L) { auto q = checkQueue(L); int n = get_len(q); lua_getuservalue(L, 1); // 描述: 创建一个新的table并将之放在栈顶 // narr是该table数组部分的长度,nrec是该table hash部分的长度. // lua_newtable (lua_State *L); 就是lua_createtable(L,0,0) lua_createtable(L, n, 0); for (int i = 0; i < n; ++i) { auto ref = q->ref[((q->head + i) % q->size)]; if (ref == LUA_REFNIL) { lua_pushnil(L); } else { lua_rawgeti(L, -2, ref); } lua_rawseti(L, -2, i + 1); } return 1; } static const struct luaL_Reg f[] = { { "new", l_new, }, { nullptr, nullptr, } }; static const struct luaL_Reg m[] = { { "__len", l_len, }, { "__gc", l_gc, }, { "__tostring", l_string, }, { "table", l_toTable, }, { "getIndex", l_index}, { "push", l_push}, { "pop", l_pop}, { nullptr, nullptr} }; LUA_API int luaopen_libQueue(lua_State *L) { luaL_newmetatable(L, QUQUE_KEY); // metatable.__index = metatable lua_pushstring(L, "__index"); lua_pushvalue(L, -2); lua_settable(L, -3); // 将所有 luaL_Reg数组中的函数注册到栈顶的table中. // 当upvalue个数不为0时,所创建的所有函数共享这些upvalue. -2到-(nup+1)的元素为要注册的upvalue. luaL_setfuncs(L, m, 0); luaL_newlib(L, f); return 1; } ``` --- ###总结 - 如果你是在C++的代码中使用Lua,`extern "C" { #include <lua.h> }` - Lua中的字符串可以不以`\0`作为结束符。这样,字符串中可以包含任意的二进制(甚至是`\0`),字符串的长度由明确的长度指定 - 在`lua_pushlstring()`、`lua_pushliteral()`以及`lua_pushstring()`中,Lua`不保存字符串(变量)指针`。因此当这些函数返回时,你就可以`修改你的字符串` - `int lua_checkstack(lua_State *L, int sz)`:入栈是否有栈空间的情况,你需要自己判断 - 遍历一个`table`时,不要将`lua_tolstring()`作用在`key`上,这样会导致`lua_next()`无法正常运行 - 对于执行失败时会返回0的`lua_to*()`类别的函数,我们最好先使用`lua_is*()`类别的函数判断参数的类型,之后再使用`lua_to*()`类别的函数对参数进行转换;而对于执行失败时会返回NULL的`lua_to*()`类别的函数,我们可以直接使用`lua_to*()`类别的函数直接对参数进行转换,判断函数的返回值非NULL与否,就能判断转换是否成功。 - `lua_pop()`就是通过`lua_settop()`实现的,`#define lua_pop(L,n) lua_settop(L, -(n)-1)` - 以下操作对于虚拟栈没有任何影响: - `lua_settop(L, -1); ` /* set top to its current value */ - `lua_insert(L, -1);` /* move top element to the top */ - `lua_replace(L, -1); ` /* replace top element by the top element */ - 通常C库中`特殊的函数`都被定义为公有的(使用`LUA_API`修饰),而其他函数均被定义为私有的(使用`static`修饰) - 当C函数接收到Lua传递的字符串参数时,有两个规则必须要遵守: - 不要将字符串参数出栈(函数返回后,Lua会负责将函数的参数以及函数本身出栈,之后将函数的返回值入栈) - 不要修改该字符串参数 - C程序创建的字符串要向Lua传递时,需要注意的东西: - 字符串缓存空间的分配以及释放 - 字符串缓存溢出 0921学习 lua c api