呼和浩特腾讯企业邮箱,邯郸seo排名,初中生电脑作业做网站,专门做项目代理的网站零、前言
Lua 作为一门脚本语言#xff0c;可以作为 “配置文件”、“动态逻辑脚本” 等角色作用于宿主程序。
因为他是一门语言#xff0c;所以他有以下的好处#xff1a;
1. Lua 会处理语法细节#xff0c;后续维护简单#xff0c;并且可以有注释。 2. 可以编写逻辑可以作为 “配置文件”、“动态逻辑脚本” 等角色作用于宿主程序。
因为他是一门语言所以他有以下的好处
1. Lua 会处理语法细节后续维护简单并且可以有注释。 2. 可以编写逻辑达到复杂的配置。
如果我们的程序需要进行一些 “下发配置” 时一般会考虑选择 “json”、“文件” 等形式。但是如果 “配置” 内容较为复杂则可以考虑 Lua 了具体可以查看以下分享。
一、运行 Lua 文件
在之前 “C 与 Lua 交互异常处理” 的文章中已分享如何在 C/C 中使用 Lua 文件这里复习一下。
可以通过 lua_call 和 lua_pcall 两个函数调用 Lua 代码。
int lua_call(lua_State *L, int nargs, int nresults);int lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);两者均用于在 C/C 代码中调用 Lua 函数不同点在于
lua_call 会将代码中的异常直接抛出导致程序中断。lua_pcall 提供一个保护模式运行 Lua 代码即使发生异常也会被捕获并可以通过第四个参数的错误处理函数处理错误程序不会因此而中断。
参数
参数 L Lua State 的指针。参数 nargs 传递给被调用函数的参数个数。参数 nresults 期望的返回值个数。参数 errfunc 错误处理函数的索引用于处理发生的错误。如果为 0则错误信息会被压入栈顶。
返回值
函数调用成功返回 0并将返回值压入栈中。如果函数调用发生错误返回一个非零值并将错误信息压入栈中。 错误处理的细节可以翻阅之前的 “C 与 Lua 交互异常处理” 文章。 举个例子
我们通过 lua_pcall 加载一个 Lua 文件然后在调用 Lua 中的一个函数计算数值最后获取返回结果。
Lua 文件的内容
function luaFunction(x, y)return (x ^ 2 * math.sin(y)) / (1 - x)
end接下来看宿主如何运行和调用可以结合着注释理解。思路是
使用 luaL_loadfile 加载 Lua 文件使用 lua_pcall 运行 Lua 文件此时 Lua 中的 luaFunction 是一个全局变量将 luaFunction 压入栈同时将需要传递的参数压入栈然后通过 lua_pcall 调用函数最后使用出栈函数获取结果因为这里为数值所以使用 lua_tonumberx 出栈函数
// C 入口
void cppCallLuaFunction() {std::string fname PROJECT_PATH /5、C调用Lua代码/调用Lua函数/调用Lua函数.lua;lua_State *L luaL_newstate();luaL_openlibs(L);// 加载 Lua 文件、运行 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);return;}double calResult 0;bool isSuccess false;// 调用 Lua 文件中的函数isSuccess callLuaFunction(L, 2, 34, calResult);if (isSuccess) {printf(调用 Lua 成功 luaFunction: %f\n, calResult);} else {printf(调用 Lua 失败\n);}lua_close(L);
}// 调用 Lua 函数
bool callLuaFunction(lua_State *L, double x, double y, double *result) {// 获取全局中的 luaFunction 变量将其压入栈中int getResult lua_getglobal(L, luaFunction);if (getResult LUA_TNIL) {printf(lua_getglobal get failure\n);return false;}// 将 x 和 y 入栈会作为 luaFunction 函数的两个参数lua_pushnumber(L, x);lua_pushnumber(L, y);// 运行 luaFunction 函数if (lua_pcall(L, 2, 1, 0) ! LUA_OK) {printf(error running function f: %s, lua_tostring(L, -1));return false;}int isNum;// 获取 luaFunction 的返回值*result lua_tonumberx(L, -1, isNum);if (!isNum) {printf(function luaFunction should return a number.);return false;}// 需要将返回值弹出lua_pop(L, 1);return true;
}// -- 调用 Lua 成功 luaFunction: -2.116331二、lua_getglobal
在上面一小节中使用到了 lua_getglobal 这里详细阐述下这一函数的作用
int (lua_getglobal) (lua_State *L, const char *name);作用
用于获取全局变量的值并将其压入 Lua 栈中。
参数
参数 L Lua 状态机Lua state指针。参数 name 要获取的全局变量的名称以字符串形式表示。
返回值
如果成功获取到全局变量则返回该变量在栈中的索引索引是从 1 开始的整数。
如果未找到指定的全局变量则返回 LUA_TNIL。
三、配置文件
在前言一节中分享用 Lua 文件作为 “配置文件”这一小节则围绕这一用法展开分享他的好处和讲解如何在 C 中调用 Lua 文件。
1、根据环境执行不同逻辑
有时我们需要根据不同的环境配置一些不同的参数。可以在 Lua 文件中通过 os.getenv 获取系统配置的环境变量进行返回不同的配置信息。
首先需要在运行的机器中配置环境变量我的电脑是 Mac 配置在 ~/.zshrc 中。
在 ~/.zshrc 配置内容如下所示
export DISPLAY_ENVMac然后在 Lua 中进行获取使用Lua 的内容如下
-- DISPLAY_ENV 在环境变量中配置open ~/.zshrc 可以查看
local displayEnv os.getenv(DISPLAY_ENV);
print(displayEnv, displayEnv)if displayEnv Mac thenwidth 3072height 1920
elsewidth 1920height 1080
end最后通过 C 加载和运行该 Lua 文件
void loadConfigUseEnv() {std::string filename PROJECT_PATH /5、C调用Lua代码/Lua作为配置文件/根据环境变量获取值/config.lua;lua_State *L luaL_newstate();luaL_openlibs(L);if (luaL_loadfile(L, filename.c_str()) || lua_pcall(L, 0, 0, 0)) {printf(cant run config. file: %s\n, lua_tostring(L, -1));return;}int width;if (!getGlobInt(L, width, width)) {printf(Get width failure.);return;}int height;if (!getGlobInt(L, height, height)) {printf(Get height failure.);return;}printf(size: %d x %d\n, width, height);lua_close(L);
}bool getGlobInt(lua_State *L, const char *var, int *result) {int isNum;// 将 var 对应的值压入栈中lua_getglobal(L, var);*result (int) lua_tointegerx(L, -1, isNum);if (!isNum) {printf(%s should be a number\n, var);return false;}// 将 var 对应值压入栈中的值弹出lua_pop(L, 1);return true;
}会输出以下内容
displayEnv Mac
size: 3072 x 1920可以看到Lua 脚本会获取我们电脑的环境变量根据值执行不同的逻辑这一点在 json、文本配置文件是无法做到的。
2、预设配置
宿主可以初始化一些配置选项给到 Lua Lua 根据所需进行使用当然也可以自行生成配置项这一过程只是业务逻辑上的设计。
下面举个例子假设需要给一个 App 配置一个主题色颜色用 rgb 进行表示在 Lua 中可以用 table 进行装载颜色让数据联系更加紧凑可以内置一些颜色给到 Lua 进行使用。
话不多说下面开始演示这一过程是如何使用。
第一步我们需要先将内置的颜色值设置到 Lua 中。 需要先创建一个表 table 然后将 key - value 放置到 table 中。
在下面的 setColor 方法中会先创建一个 table 然后通过封装的 setColorField 方法将 red、green、blue 放置到 table 中。
setColorField 中会将 key、value 按顺序压栈然后通过 lua_settable 设置到对应的索引表中。 lua_newtable、lua_createtable、lua_settable、lua_setfield 这些 api 会在接下来的小节进行详细讲解 void setColor(lua_State *L, struct ColorTable *ct) {// 创建一个空表并压入栈顶// lua_newtable 这是一个宏真是定义是 lua_createtable(L, 0, 0)// lua_newtable 和 lua_createtable 会压入一个 table 到栈中lua_newtable(L);
// lua_createtable(L, 0, 3);setColorField(L, red, ct-red);setColorField(L, green, ct-green);setColorField(L, blue, ct-blue);// name table// 弹出表并且将其设置为指定名称的全局变量的值lua_setglobal(L, ct-name);
}void setColorField(lua_State *L, const char *index, int value) {// 第一种// 键lua_pushstring(L, index);// 值lua_pushnumber(L, (double) value / MAX_COLOR);// 将键和值弹出然后设置到 table 索引为 -3 中table[stack[-2]] stack[-1]lua_settable(L, -3);// 第二种
// lua_pushnumber(L, (double) value / MAX_COLOR);
// lua_setfield(L, -2, index);
}经过这一步的设置在 Lua 中就可以查看到对应的值了。
第二步加载和运行 Lua 文件在 Lua 文件中使用第一步设置的值将其返回给宿主
Lua 文件内容如下
print(PINK, PINK.red, PINK.green, PINK.blue)
-- c 获取到的为 table
background PINK
-- c 获取到的为 string
--background PINK加载和运行的代码如下
#define MAX_COLOR 255struct ColorTable {char *name;unsigned char red, green, blue;
} colorTable[] {{WHITE, MAX_COLOR, MAX_COLOR, MAX_COLOR},{RED, MAX_COLOR, 0, 0},{GREEN, 0, MAX_COLOR, 0},{BLUE, 0, 0, MAX_COLOR},{PINK, 255, 192, 203},{nullptr, 0, 0, 0}
};void load(lua_State *L, const char *fname) {int i 0;// 设置颜色值while (colorTable[i].name ! nullptr) {setColor(L, colorTable[i]);}if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0)) {printf(cant run config. file: %s\n, lua_tostring(L, -1));return;}// ... 获取值的操作下面分享
}void loadConfigUseTable() {std::string filename PROJECT_PATH /5、C调用Lua代码/Lua作为配置文件/配置中使用表/config.lua;lua_State *L luaL_newstate();luaL_openlibs(L);load(L, filename.c_str());lua_close(L);
}第三步获取 Lua 返回的值
将 Lua 中对应的 “background” 变量压入栈然后将 key 压入栈通过 lua_gettable 获取对应的值。
bool getColorField(lua_State *L, const char *key, int *result) {int isNum;// 第一种做法// 将 key 压入栈lua_pushstring(L, key);// 将栈顶key弹出然后将读取的值压入lua_gettable(L, -2);// 第二种做法lua_getfield 和 lua_gettable 都会返回类型
// if (lua_getfield(L, -1, key) LUA_TNUMBER) {
// printf(invalid component in background color);
// return false;
// }*result (int) (lua_tonumberx(L, -1, isNum) * MAX_COLOR);if (!isNum) {printf(invalid component %s in color, key);return false;}lua_pop(L, 1);return true;
}void load(lua_State *L, const char *fname) {// ... 加载和运行 Lua 文件// 将 background 压入栈lua_getglobal(L, background);if (lua_istable(L, -1)) {int red;if (!getColorField(L, red, red)) {printf(Get red failure.\n);return;}int green;if (!getColorField(L, green, green)) {printf(Get green failure.\n);return;}int blue;if (!getColorField(L, blue, blue)) {printf(Get blue failure.\n);return;}printf(table color: (%d, %d, %d)\n, red, green, blue);} else if (lua_isstring(L, -1)) {const char *name lua_tostring(L, -1);int i;for (i 0; colorTable[i].name ! nullptr; i) {if (strcmp(name, colorTable[i].name) 0) {break;}}if (colorTable[i].name nullptr) {printf(invalid color name (%s), name);return;}int red colorTable[i].red;int green colorTable[i].green;int blue colorTable[i].blue;printf(string color: (%d, %d, %d)\n, red, green, blue);} else {printf(background is not a table.);}
}最后会输出
table: 0x600001afc880 1.0 0.75294117647059 0.79607843137255
table color: (255, 192, 203)四、lua_newtable
void lua_newtable(lua_State *L);作用
用于创建一个新的空表并将其压入 Lua 栈中。
参数
参数 L Lua 状态机Lua state指针。
返回值
没有返回值但会压入一个 table 在栈顶。
五、lua_createtable
lua_newtable 其实是一个宏定义真正实现是 lua_createtable
#define lua_newtable(L) lua_createtable(L, 0, 0)void (lua_createtable) (lua_State *L, int narr, int nrec);作用
用于创建一个新的表并将其压入 Lua 栈中。
参数
参数 L Lua 状态机Lua state指针。参数 narr 表的数组部分初始容量。数组部分是用于存储连续整数键索引的区域。当按顺序插入整数键时它们会被存储在数组部分中以实现高效的数组访问。narr 的值可以是正整数用于指定表初始化时预分配的数组部分的大小。如果 narr 为 0则表示不分配数组部分。参数 nrec 表的哈希部分初始容量。哈希部分是用于存储非整数键如字符串键的区域。当我们插入非整数键时它们会被存储在哈希部分中并使用哈希表来实现高效的键值对查找。nrec 的值可以是正整数用于指定表初始化时预分配的哈希部分的大小。如果 nrec 为 0则表示不分配哈希部分。 一般情况下如果事先知道表的大致大小设置适当的 narr 和 nrec 值可以提高表操作的效率。但如果不确定表的大小或者表的大小会动态变化设置为 0 会让 Lua 在需要时自动调整表的大小。Lua 在内部会根据需要动态调整表的大小以适应实际的键值对数量。 返回值
没有返回值但会压入一个 table 在栈顶。
六、lua_gettable
int (lua_gettable) (lua_State *L, int idx);作用
用于从 Lua 栈中获取表中指定键的值并将该值压入栈顶
参数
参数 L Lua 状态机Lua state指针。参数 index 表示要获取表的索引位置。如果索引为正数则表示从栈底向上数的位置获取表如果索引为负数则表示从栈顶向下数的位置获取表。
返回值
返回数据类型并且会将获取到的值压入栈顶如果没有找到对应的值则会将 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一图胜千言
七、lua_getfield
和 lua_gettable 的作用一样只是更加便捷不用额外的将 key 压入栈顶。
int (lua_getfield) (lua_State *L, int idx, const char *k);作用
用于获取指定表索引为 idx 中字段名为 k 的值。
参数
参数 L Lua 状态机的指针。参数 index 表在堆栈中的索引。如果索引为正数则表示从栈底向上数的位置获取表如果索引为负数则表示从栈顶向下数的位置获取表。参数 k 字段的名称以 C 字符串的形式传递。
返回值
返回数据类型并且会将获取到的值压入栈顶如果没有找到对应的值则会将 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八、lua_settable
void lua_settable(lua_State *L, int index);作用
用于将键索引为 -2 、值索引为 -1 对存储到表索引为 index 中。
可以理解为 table[stack[-2]] stack[-1] table 为 stack[index] 。
参数
参数 L Lua 状态机Lua state指针。参数 index 表示要获取表的索引位置。如果索引为正数则表示从栈底向上数的位置获取表如果索引为负数则表示从栈顶向下数的位置获取表。
返回值
没有返回值会将键和值出栈。
九、lua_setfield
和 lua_settable 的作用一样只是更加便捷不用将 key 值压入到栈中但还是需要将 value 压至栈顶。
void lua_setfield(lua_State *L, int index, const char *key);作用
用于将值索引为 -1 存储到指定表索引为 index 中的指定键名key。
可以理解为 table[index] stack[-1] table 为 stack[index] 。
参数
参数 L Lua 状态机Lua state指针。参数 index 表在栈中的索引位置。如果索引为正数则表示从栈底向上数的位置获取表如果索引为负数则表示从栈顶向下数的位置获取表。参数 key 作为键的字符串。是一个 C 字符串表示要在表中使用的键名。
返回值
没有返回值会将栈顶的 value 出栈。
十、lua_setglobal
void lua_setglobal(lua_State *L, const char *name);作用
用于将值索引为 -1 以 name 作为属性名存储到全局变量中。
可以理解为 name value 。
参数
参数 L Lua 状态机Lua state指针。参数 name 是一个 C 字符串表示要存储值的全局变量名。
返回值
没有返回值会将栈顶值弹出。
十一、写在最后
Lua 项目地址Github传送门 (如果对你有所帮助或喜欢的话赏个star吧码字不易请多多支持)
如果觉得本篇博文对你有所启发或是解决了困惑点个赞或关注我呀。
公众号搜索 “江澎涌”更多优质文章会第一时间分享与你。