Lua在设计之初就是为了嵌入到应用程序中,为这些应用程序提供灵活的扩展和定制功能。Lua的核心是用C语言编写的,所以Lua脚本可以很容易地与C/C++代码进行交互,通过Lua脚本,用户可以在不修改原有C/C++代码的基础上,实现功能的扩展和定制。
在C/C++程序中可以使用Lua来编写一些需要频繁修改的逻辑,而不需要重新编译整个C/C++程序,从而加快开发周期。对于一些复杂的逻辑,使用Lua脚本实现可能比直接用C/C++编写更加简单和直观。在以上基础上Lua脚本还可以快速实现和测试新的想法和功能,快速完成原型开发。
编写C/C++代码
在C/C++中嵌入Lua代码的流程如下:
- 初始化Lua环境:使用Lua提供的API(
luaL_newstate
)创建一个新的Lua环境(Lua State)。 - 加载Lua脚本:使用Lua的C API(
luaL_dofile
)加载并执行Lua脚本文件。 - 调用Lua函数:通过Lua的C API(
lua_getglobal
、lua_pushnumber
、lua_pcall
等)调用Lua脚本中定义的函数,并传递参数。 - 获取返回值:Lua函数执行完毕后,使用Lua的C API(
lua_tonumber
、lua_tostring
等)从栈中获取返回值。 - 关闭Lua环境:使用Lua的C API(
lua_close
)关闭Lua环境,释放相关资源。
Lua和C/C++之间的交互主要依赖于一个虚拟栈(Lua State)。这个栈是先进后出的,用于在Lua和C/C++之间传递数据和函数参数。当C/C++代码需要调用Lua函数或访问Lua变量时,它会将数据压入栈中;Lua执行完毕后,会将结果压回栈中,供C/C++代码读取。这种机制使得Lua和C/C++之间的交互变得简单而高效。
在Lua与C/C++的交互过程中,需要进行数据类型转换。比如将C语言中的整数类型转换为Lua中的数值类型,或将Lua中的表转换为C语言中的结构体等。在注册到Lua环境中的C函数和扩展模块中,需要对数据进行正确的类型转换。
C语言示例
先新建一个test.c文件,将以下内容复制到文件中:
// test.c #include <lua.h> #include <lualib.h> #include <lauxlib.h> int main(void) { lua_State *L = luaL_newstate(); // 创建Lua状态机 luaL_openlibs(L); // 打开Lua标准库 // 执行Lua脚本 const char *lua_script = "print('Hello gay boy!')"; if (luaL_dostring(L, lua_script) != LUA_OK) { const char *message = lua_tostring(L, -1); printf("Error executing Lua script: %s\n", message); lua_pop(L, 1); // 弹出错误消息 } lua_close(L); // 关闭Lua状态机 return 0; }
C++示例
先新建一个test.cpp文件,将以下内容复制到文件中:
#include <iostream> #include <lua.hpp> int main() { lua_State* L = luaL_newstate(); // 初始化Lua状态机 luaL_openlibs(L); // 打开Lua标准库 const char* luaCode = "print('Hello from Lua!')"; if (luaL_dostring(L, luaCode)) { std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl; lua_pop(L, 1); // 移除错误消息 return 1; // 返回错误码 } // 关闭Lua状态机 lua_close(L); return 0; }
编译C/C++文件
C语言文件编译
在Linux和Windows系统中,都需要先找到lua.h的安装路径,Windows系统为path环境变量中保存的路径,我使用的Ubuntu系统,路径如下:
在找到安装路径后,可以使用以下命令编译并运行嵌入了Lua代码的C源文件,在-I后面需要替换为自己的实际路径
gcc test.c -I/usr/include/lua5.4/ -llua5.4 -o test
./test
运行结果如下:
Windows使用以下命令:
在Windows中,链接库时通常使用库的完整名称,包括前缀和后缀。如果使用的是MinGW或类似工具链,需要指定-llua
对应的完整库名,如-llua54
或-llua
,具体取决于库文件(如lua54.dll
或lua.dll
)的实际名称。
gcc test.c -IC:\path\to\lua\include -LC:\path\to\lua\lib -llua54 -o test
这里: -IC:\path\to\lua\include
指定了Lua头文件的位置。
-LC:\path\to\lua\lib
指定了Lua库文件的位置
C++语言文件编译
C++语言嵌入Lua语言需要找到lua.hpp文件,编译命令与C语言文件编译方式相似:
g++ test.cpp -I/usr/include/lua5.4/ -llua5.4 -o test ./test
在VS Code中调试
这里因为没有在VS Code中配置头文件导入路径,会产生报错曲线:
这里可以点击左侧的debug图标,随后点击 运行和调试、创建launch.json
在项目文件夹中会出现.vscode文件夹,文件夹中存在以下三个文件:
tasks.json:该文件用于配置编译任务。它指定了编译命令、编译参数以及输出文件的位置。对于多文件项目,需要确保tasks.json中包含了所有相关的源文件。
launch.json:该文件用于配置调试任务。它指定了调试器的类型(如gdb或lldb)、调试请求的类型(如launch或attach)、程序路径、工作目录以及其他调试选项。在调试过程中,VS Code会根据launch.json中的配置启动调试器,加载可执行文件,并在设置的断点处暂停执行。
c_cpp_properties.json:通过配置编译器路径、头文件搜索路径、预处理器宏定义等信息,支持VSCode中的IntelliSense功能,提供高效的代码补全、代码导航和调试支持。
C语言版本
首先将包含 lua.h 的文件夹安装路径添加到c_cpp_properties.json中
"/usr/include/lua5.4/", "/home/astra/luajit/src/"
完整的c_cpp_properties.json内容如下:
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/usr/include/lua5.4/", "/home/astra/luajit/src/" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c17", "cppStandard": "gnu++17", "intelliSenseMode": "linux-gcc-x64" } ], "version": 4 }
将以下编译命令添加到tasks.json中,这里需要自己的lua版本进行修改
"-I/usr/include/lua5.4/", "-llua5.4",
完整的tasks.json内容如下:
{ "tasks": [ { "type": "cppbuild", "label": "C/C++: gcc build active file", "command": "/usr/bin/gcc", "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}", "-I/usr/include/lua5.4/", "-llua5.4", ], "options": { "cwd": "${fileDirname}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "Task generated by Debugger." } ], "version": "2.0.0" }
点击右上角的调试按钮,即可对代码进行调试
C/C++版本
调试C++文件需要在上述json文件中加入连接到C++标准库的显示连接
"-lstdc++" // 显式链接到C++标准库
launch.json
{ "version": "0.2.0", "configurations": [ { "name": "C++ Lua Debug", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}", "-I/usr/include/lua5.4/", "-llua5.4", "-lstdc++" // 显式链接到 C++ 标准库 ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "C/C++: gcc build active file", "miDebuggerPath": "/usr/bin/gdb" } ] }
tasks.json
{ "version": "0.2.0", "configurations": [ { "name": "C++ Lua Debug", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}", "-I/usr/include/lua5.4/", "-llua5.4", "-lstdc++" // 显式链接到C++标准库 ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "Enable pretty-printing for gdb", "text": "-enable-pretty-printing", "ignoreFailures": true } ], "preLaunchTask": "C/C++: gcc build active file", "miDebuggerPath": "/usr/bin/gdb" } ] }
c_cpp_properties.json
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/usr/include/lua5.4/", "/home/astra/luajit/src/" ], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c17", "cppStandard": "gnu++17", "intelliSenseMode": "linux-gcc-x64" } ], "version": 4 }