做代练网站能备案,网站做两个月百度没有录取,温州建设局网站首页,网站开发与应用是什么零、前言
使用 Lua 时#xff0c;在编写 C/C 函数经常需要对栈进行交互#xff0c;而这中间更多的操作和数组、字符串相关。
一、数组操作的便捷方式
从之前分享的 “Lua 数据类型——表” 文章中知道 Lua 中的 “数组” 是以表的形式存在#xff0c;只是他的 key 值是有…零、前言
使用 Lua 时在编写 C/C 函数经常需要对栈进行交互而这中间更多的操作和数组、字符串相关。
一、数组操作的便捷方式
从之前分享的 “Lua 数据类型——表” 文章中知道 Lua 中的 “数组” 是以表的形式存在只是他的 key 值是有序的数值。
对于 Lua 的数组存取当然可以使用之前介绍的 lua_gettable 和 lua_settable 的方法只是较为繁琐。所以 Lua 针对数组提供了以下的方法进行快捷的存取
// Lua 5.3 之后才有
// 会调用 table 元方法
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);// 不调用 table 元方法
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);// Lua 5.3 之后才有
// 会调用 table 的元方法
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);// 不调用 table 元方法
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);接下来一个个讲解
1、lua_seti 和 lua_rawseti
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);描述
用于将栈顶的元素即索引为 -1 作为 value 用 n 作为 key 设置到索引为 idx 的 table 中。
参数
L Lua 状态机Lua state的指针。idx 要操作的 Lua 表在栈中的索引位置。n 设置在 table 中的 key 值必须是整型。
返回值
没有返回栈顶的元素会被使用后出栈。
两个函数的区别
如果操作的 table 没有元表则效果是一样的使用 lua_rawseti 会稍微快一些。
如果操作的 table 有元表则 lua_seti 会使用到 table 的元方法。
和 lua_settable 相同的用法
lua_seti 只是针对 key 为整型的 table 设置更加便捷当然也可以用 lua_settable 实现一样的效果
lua_pushstring(L, value);
lua_seti(L, t, key);// 当 t 为正整数时等同于以下操作。
// 因为 t 为负数的话lua_settable 的 t 要要深一个元素lua_settable 的操作需要将 key 压入栈。lua_pushnumber(L, key);
lua_pushstring(L, value);
lua_settable(L, t);2、lua_geti 和 lua_rawgeti
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);描述
用于从索引为 idx 位置的 table 中获取 key 为 n 的值将其压入栈中即栈顶。
参数
L Lua 状态机Lua state的指针。idx 要操作的 Lua 表在栈中的索引位置。n 要获取 table 的 key 值必须是整型。
返回值
返回 value 数据类型并且会将获取到的值压入栈顶如果没有找到对应的值则会将 nil 压入因为在 table 中查询一个不存在的键时则会返回 nil 。
返回的数据类型有以下类型可以根据类型判断是否符合期望。
#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两个函数的区别
如果操作的 table 没有元表则效果是一样的使用 lua_rawgeti 会稍微快一些。
如果操作的 table 有元表则 lua_geti 会使用到 table 的元方法。
和 lua_settable 相同的用法
lua_geti 只是针对 key 为整型这一类型 table 的设置更加便捷当然也可以用 lua_gettable 实现一样的效果
lua_geti(L, t, key)// 当 t 为正整数时等同于以下操作。
// 因为 t 为负数的话lua_gettable 的 t 要要深一个元素lua_gettable 的操作需要将 key 压入栈。lua_pushnumber(L, key);
lua_gettable(L, t);3、举个例子
以下例子实现了这几个步骤
创建一个 table 并压入栈中。使用两种方式向 table 中设置元素。使用两种方式获取 table 中的元素。打印 table 的长度。
lua_State *L luaL_newstate();// 创建并压入一个新表
lua_newtable(L);// --------- 添加至数组的操作方式 ---------
// 第一种
// table[1] 江澎涌!快捷方式插入
lua_pushstring(L, 江澎涌!快捷方式插入);
lua_seti(L, -2, 1);// 第二种
// 栈底 ---- 栈顶
// table - key - value
// 使用完后key - value 会被弹出
// table[2] 江澎涌!常规方式插入
lua_pushnumber(L, 2);
lua_pushstring(L, 江澎涌!常规方式插入);
lua_settable(L, -3);// --------- 从数组中获取值方式 ---------
// 第一种
// 获取 table 中索引为 1 的值压入栈
printf(lua_geti(L, -1, 1) 类型%d\n, lua_geti(L, -1, 1));
printf(table[1] %s\n, lua_tostring(L, -1));
// 将获取的值弹出
lua_pop(L, 1);// 第二种
// 压入 key 值
lua_pushnumber(L, 2);
// 获取 table 中索引为 2 的值压入栈
printf(lua_gettable(L, -2) 类型%d\n, lua_gettable(L, -2));
printf(table[2] %s\n, lua_tostring(L, -1));
// 将获取的值弹出
lua_pop(L, 1);long long n luaL_len(L, 1);
printf(lua table length: %lld\n, n);lua_close(L);输出以下内容
lua_geti(L, -1, 1) 类型4
table[1] 江澎涌!快捷方式插入
lua_gettable(L, -2) 类型4
table[2] 江澎涌!常规方式插入
lua table length: 24、luaL_len
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);描述
用于获取给定索引 idx 位置的 Lua 值的长度length可以用于获取字符串、表和用户数据等类型的长度。
参数
L Lua 状态机Lua state的指针。idx 要获取长度的值在栈中的索引位置。
返回值
会对该索引位置的元素执行长度运算符然后将其长度值整数返回。
但由于元方法的存在该运算符有可能会返回任意类型的值所以如果不是整型则会发生错误。
二、字符串操作
1、字符串普通操作的 C-API
在 C/C 和 Lua 的交互中字符串的使用也是比较频繁的但由于内存回收的问题使用过程中需要以下几点
当 C/C 接收到一个 Lua 字符串为参数时需要遵循在使用该字符串期间不能从栈中将其弹出而且不修改字符串。当需要将 C/C 的字符串传递给 Lua 时Lua 提供了很多关于字符串的 C-API 给我们使用Lua 内部会进行相应的内存分配和管理。
Lua 提供操作字符串的 C-API
C API 函数描述const char *(lua_pushstring) (lua_State *L, const char *s);将 字符串以 “\0” 结尾 压栈。const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);将 字符串会结合 “\0” 和长度的参数决定字符串的长度 压栈。const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);将 字符串格式化字符串接收可变参数 压栈。const char *(lua_pushfstring) (lua_State *L, const char *fmt, …);将 字符串格式化字符串 压栈。void (lua_concat) (lua_State *L, int n);会将栈顶的 n 个值弹出然后进行连接最后将结果压入栈中。 在之前分享的 《C 与 Lua 数据交互载体——栈》 中已经展示了如何使用这些 API 这里就不再一一罗列如何使用。 lua_pushfstring 可以接受以下的指示符
指示符描述%s插入一个以 \0 结尾的字符串%d插入一个 int%f插入一个 Lua 语言的浮点数%p插入一个浮点数%I插入一个 Lua 语言的整型数%c插入一个以 int 表示的单字节字符%U插入一个以 int 表示的 UTF-8 字节序列%%插入一个百分号 lua_pushfstring 和 lua_concat 都可以简便的将多个字符串进行连接但是如果如果字符串数量较大时效率会比较低可以考虑使用缓冲机制下面的小节会进行分享。 2、举个例子——字符串截取
这个例子的功能用 C 函数实现一个分割字符串的功能给到 Lua 进行调用
第一步定义分割字符串的 C 函数。
接收和检查 “需要被分割的字符串” 和 “分割符” 。创建一个 table 并且压入到栈中后续的分割结果会压入到该 table 中。使用 C-API strchr 对字符串进行检索被分割的位置然后使用 lua_pushlstring 进行对字符串裁剪后压栈。使用 lua_rawseti 进行设置到 “第二步创建的 table ” 中。循环结束后将剩余的字符串也设置到 table 中。返回 1 表示只有一个返回值。
int lua_split(lua_State *L) {// 被分割的内容const char *s luaL_checkstring(L, 1);// 分割符const char *sep luaL_checkstring(L, 2);// 创建一个 table 并且压入到栈中后续的分割结果会压入到该 table 中lua_newtable(L);const char *e;int i 1;// char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c一个无符号字符的位置// 该函数返回在字符串 str 中第一次出现字符 c 的位置如果未找到该字符则返回 NULLwhile ((e strchr(s, *sep)) ! nullptr) {// 将 s 中 e - s 个字符压入栈顶lua_pushlstring(L, s, e - s);// 设置到 -2 table 中lua_rawseti(L, -2, i);s e 1;}// 把最后的字符也压入lua_pushstring(L, s);lua_rawseti(L, -2, i);return 1;
}第二步编写 Lua 脚本。
Lua 脚本很简单只是调用一下第三步注入的 C 函数函数内容为第一步编写的函数然后打印 table 长度和内容
local result split(江_澎_涌, _);print(length, #result);for i, v in ipairs(result) doprint(i, --- , v)
end第三步将函数注入到 Lua 中并运行 Lua 的脚本。
lua_State *L luaL_newstate();
luaL_openlibs(L);lua_pushcfunction(L, lua_split);
lua_setglobal(L, split);std::string fname PROJECT_PATH /8、编写C函数技巧/字符串便捷操作/字符串截取.lua;
if (luaL_loadfile(L, fname.c_str()) || lua_pcall(L, 0, 0, 0)) {printf(cant run config. file: %s\n, lua_tostring(L, -1));
}lua_close(L);最后输出的内容如下
length 3
1 --- 江
2 --- 澎
3 --- 涌3、lua_concat 的使用
lua_concat 会将栈顶的 n 个值弹出然后进行连接最后将结果压入栈中。
举个例子我们压入 3 个字符串然后使用 lua_concat 进行弹出拼接并压入栈中最后将这个结果打印。
lua_State *L luaL_newstate();// 压入三个字符串
lua_pushstring(L, 江!);
lua_pushstring(L, 澎!);
lua_pushstring(L, 涌!);
// 将三个字符串进行弹出并拼接
lua_concat(L, 3);printf(stack top: %d\n, lua_gettop(L));
printf(concat: %s\n, lua_tostring(L, -1));lua_close(L);最后输出为
stack top: 1
concat: 江!澎!涌!4、缓冲机制
如果操作的字符串数量较大可以考虑使用缓冲机制。
4-1、已知长度的缓冲区
对于长度已知的情况一般分为以下几个步骤
声明一个 luaL_Buffer 类型的变量。使用 luaL_buffinitsize 进行初始化获取一个指向指定大小缓冲区的指针后续就可以自由地使用该缓冲区来创建字符串。最后调用 luaL_pushresultsize 将缓冲区中的内容转换为一个新的 Lua 字符串并将该字符串压栈。
int str_upper(lua_State *L) {size_t l;// 检测参数是否为字符串并且获取长度const char *s luaL_checklstring(L, 1, l);size_t i;// 声明缓冲区luaL_Buffer b;// 初始化缓冲区char *p luaL_buffinitsize(L, b, l);for (i 0; i l; i) {p[i] toupper(s[i]);}// 将缓冲区中的内容转换为一个新的 Lua 字符串并将字符串压栈luaL_pushresultsize(b, l);
}调用的代码如下这里为了方便就省去了从 Lua 进行调用直接通过 C 压入字符串给到上述的函数进行使用。
lua_State *L luaL_newstate();lua_pushstring(L, jiang peng yong);
str_upper(L);
printf(to upper: %s\n, lua_tostring(L, -1));lua_close(L);输出的内容如下
to upper: JIANG PENG YONGstr_upper 其实是 Lua 中 lstrlib.c 的源代码感兴趣可以自行查阅。 luaL_pushresultsize 的调用并未传入 lua_State 类型的参数是因为初始化之后缓冲区保存了对 lua_State 状态的引用。 4-2、未知长度的缓冲区
如果不知道最终需要多大的缓冲区可以通过逐步增加内容的方式来使用缓冲区。Lua 为此提供了以下 C-API
函数描述void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);初始化缓冲区但不设置大小char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);为缓冲区分配内存空间以容纳指定大小的数据。sz 即预分配的大小以字节为单位返回指向缓冲区的指针void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);增加一个长度明确的字符串void (luaL_addstring) (luaL_Buffer *B, const char *s);用于增加一个以 \0 结尾的字符串void (luaL_addvalue) (luaL_Buffer *B);用于将栈顶的字符串放入缓冲区void (luaL_pushresult) (luaL_Buffer *B);刷新缓冲区并在栈顶留下最终的结果字符串luaL_addchar(B,c)是一个宏定义用于增加单个字符
举个例子
实现一个简易的将 table 内容连接为字符串功能。
第一步实现拼接的 C 函数。
检测第一个参数是否为 table 不是则会抛异常到 Lua 中获取 table 的长度初始化缓冲区不进行大小的设置循环 table 将内容添加到缓冲区中将缓冲区内容组装为字符串后压入栈返回 Lua
int bufferConcat(lua_State *L) {luaL_Buffer b;long long i, n;// 检测是否为 tableluaL_checktype(L, 1, LUA_TTABLE);// 获取 table 长度n luaL_len(L, 1);// 初始化缓冲区luaL_buffinit(L, b);// 循环取出 table 的值添加到缓冲区中for (i 1; i n; i) {lua_geti(L, 1, i);luaL_addvalue(b);}// 将缓冲区内容组装为字符串压入栈luaL_pushresult(b);return 1;
}第二步实现 Lua 脚本内容。
Lua 脚本中的内容如下只是简单的调用 C 暴露的函数然后将其打印。
print(concat({江, 澎, 涌, 29}, concat({江, 澎, 涌, 29}))第三步注入第一步函数到 Lua 中然后运行第二步的脚本。
lua_State *L luaL_newstate();
luaL_openlibs(L);lua_pushcfunction(L, bufferConcat);
lua_setglobal(L, concat);std::string fname PROJECT_PATH /8、编写C函数技巧/字符串便捷操作/字符串连接缓冲区.lua;
if (luaL_loadfile(L, fname.c_str()) || lua_pcall(L, 0, 0, 0)) {printf(cant run config. file: %s\n, lua_tostring(L, -1));
}lua_close(L);输出的内容如下
concat({江, 澎, 涌, 29} 江澎涌29三、写在最后
Lua 项目地址Github传送门 (如果对你有所帮助或喜欢的话赏个star吧码字不易请多多支持)
如果觉得本篇博文对你有所启发或是解决了困惑点个赞或关注我呀。
公众号搜索 “江澎涌”更多优质文章会第一时间分享与你。