一、Lua 定位
嵌入式语言:C 语言拥有控制权, Lua 语言被用作库
可扩展语言:Lua 语言拥有控制权,C 语言被用作库
无论哪一种,都需要用到 C API 进行交互。
C API 中大多数函数不会检查参数的正确性,所以必须保证在调用前确保参数的合法性,一旦出错,程序会崩溃而不会收到规范的错误信息。
可以通过使用宏定义 LUA_USE_APICHECK 来启用一些一致性检查
二、什么是 C API
C API 包括读写 Lua 全局变量的函数、调用 Lua 函数的函数、运行 Lua 代码段的函数、以及注册 C 函数(用于后面可以被 Lua 代码调用)的函数等。
通过 C API 就可以打通了 C 调用 Lua 以及 Lua 调用 C 的渠道。
三、将 Lua 集成到 C++ 项目中
第一步,下载需要的 Lua 版本源码
进入官网 www.lua.org/ftp/ 进行下载,我使用的是 5.4.4
下载后解压,进入到 src 目录,将目录下的文件复制到项目中的一个文件夹下。
以 Lua_CPP_2022
项目为例,放置在项目下的 lua_lib/lua-5.4.4
目录中。
第二步,编写 cmake 将源文件链接
# 设置 LUA_DIR 指向 lua-5.4.4 目录
set(LUA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lua-5.4.4)
# 设置 Lua 源码
set(LUA_RUNTIME_SOURCES
"${LUA_DIR}/lapi.c"
"${LUA_DIR}/lauxlib.c"
"${LUA_DIR}/lbaselib.c"
"${LUA_DIR}/lcode.c"
"${LUA_DIR}/lcorolib.c"
"${LUA_DIR}/lctype.c"
"${LUA_DIR}/ldblib.c"
"${LUA_DIR}/ldebug.c"
"${LUA_DIR}/ldo.c"
"${LUA_DIR}/ldump.c"
"${LUA_DIR}/lfunc.c"
"${LUA_DIR}/lgc.c"
"${LUA_DIR}/linit.c"
"${LUA_DIR}/liolib.c"
"${LUA_DIR}/llex.c"
"${LUA_DIR}/lmathlib.c"
"${LUA_DIR}/lmem.c"
"${LUA_DIR}/loadlib.c"
"${LUA_DIR}/lobject.c"
"${LUA_DIR}/lopcodes.c"
"${LUA_DIR}/loslib.c"
"${LUA_DIR}/lparser.c"
"${LUA_DIR}/lstate.c"
"${LUA_DIR}/lstring.c"
"${LUA_DIR}/lstrlib.c"
"${LUA_DIR}/ltable.c"
"${LUA_DIR}/ltablib.c"
"${LUA_DIR}/ltm.c"
"${LUA_DIR}/lua.c"
"${LUA_DIR}/lundump.c"
"${LUA_DIR}/lutf8lib.c"
"${LUA_DIR}/lvm.c"
"${LUA_DIR}/lzio.c"
)
add_library(
LuaLib
SHARED
${LUA_RUNTIME_SOURCES}
)
第三步,将 cmake 文件链接到主项目
# 将 lua_lib/lua-5.4.4 目录添加至编译器的搜索目录中
# 这样使用 Lua 的源文件时,#include 的文件路径就不需要使用 "lua_lib/lua-5.4.4" 了
# 如果没有这一句,使用 "lua.hpp" ,就需要 #include "lua_lib/lua-5.4.4/lua.hpp"
# 有了这一句,就只需要 #include "lua.hpp"
include_directories(lua_lib/lua-5.4.4)
# 将子目录添加到构建中
# 参数 source_dir( 即这里的 lua_lib )指定源 CMakeLists.txt 和代码文件所在的目录
# 这样就会把 lua_lib/CMakeLists.txt 加入到构建中
add_subdirectory(lua_lib)
# 添加链接库
target_link_libraries(Lua_CPP_2022 LuaLib)
这样就将 Lua 源代码链接进我们的项目了
第四步,使用以下代码进行验证是否集成成功
#import "lua.hpp"
/**
* 验证 Lua 集成是否成功
*/
void verification() {
int error;
lua_State *L = luaL_newstate();
luaL_openlibs(L);
error = luaL_loadstring(L, "print(\"jiang\")") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s\n", lua_tostring(L, -1));
lua_pop(L, 1);
}
lua_close(L);
}
int main() {
verification();
return 0;
}
// --> jiang
四、引入的头文件简介
如果是使用 C 语言的话,则直接使用以下进行引用 Lua 头文件
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
如果使用 C++ 则需要嵌套 extern ,或是直接引用 lua.hpp
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
// 或是使用 lua.hpp
#import "lua.hpp"
其实 lua.hpp 内部也就是用 extern 进行嵌套这些头文件。
1、lua.h
lua.h
中为 Lua 提供的基础函数,所有的函数都以 lua_
开头。
其中包括了 “创建新 Lua 环境的函数”、“调用 Lua 函数的函数”、“读写环境中的全局变量的函数”,以及 “注册供 Lua 语言调用的新函数的函数” 等等。
2、luaxlib.h
是一个辅助库,是基于 lua.h 提供的基础 API 提供更高层次的抽象,函数都以 luaL_
开头。
luaxlib.h 提供的是对常见任务的实用性,lua.h 提供的是经济性和正交性。
3、lualib.h
Lua 中默认不带有任何的标准库,而所有的标准库都被打包成不同的包。lualib.h 中包含了打开这些库的函数
使用 luaL_openlibs
则打开了所有的标准库。
五、 lua_State
Lua 标准库中没有定义任何 c 语言全局变量,所有状态都保存在动态结构体 lua_Stata 中,而 Lua 中的所有函数都有接收 lua_State 类型的参数,从而可以实现多线程。
宿主程序 C/C++ 会通过 lua_State 跟 Lua 进行交互,一般的操作步骤为如下所示:
// 第一步:创建 lua_State
lua_State *L = luaL_newstate();
// 第二步:打开所有标准库
luaL_openlibs(L);
// 第三步:通过 lua_State 进行 Lua 的交互
// 第四步:关闭 lua_State
lua_close(L);
当然第三步的逻辑会很多,lua_State
可以被一直持有,一直到不需要的时候再执行第四步进行关闭释放内存。
第三步的 C/C++ 与 Lua 交互细节后续会有文章详细分享。
1、标准库
上面所述的标准库是指 Lua 中的 string
、math
等标准库,Lua 为了让语言最为精炼,所以初始化的时候是不默认加载的。
lua_openlibs
会加载全部的标准库,如果不需要加载全部,可以使用以下的函数进行加载需要的标准库:
函数 | 描述 |
---|---|
luaopen_base | 基础模块,包括: - 全局函数(例如 print 等) - 全局常量(nil 等) - 核心库函数(load 等) - 字符串操作函数 - 数学函数 - 表操作函数 - 迭代器函数(ipairs 等) - 错误处理函数(error 等) |
luaopen_package | 包管理模块 |
luaopen_coroutine | 协程模块 |
luaopen_table | 表操作模块 |
luaopen_io | 输入输出模块 |
luaopen_os | 操作系统模块 |
luaopen_string | 字符串处理模块 |
luaopen_math | 数学库模块 |
luaopen_utf8 | utf8 编码处理模块 |
luaopen_debug | 调试模块 |
六、写在最后
Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)
如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀。
公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。