(记录一次尝试在C++中调用Lua的经历,最后效果貌似正确,但不确定方式是否是最佳的)
目标
Lua和Python同为脚本语言,在嵌入一个宿主应用(如C++程序)时,Lua的一个优点是更轻量级。我没有Lua的实际开发经验,但我对其感兴趣,想试一试它。
本篇的目标类似《实践在C++中调用Python函数》,希望能在在C++中调用Lua。
方式是什么?
在《实践在C++中调用Python函数》里,Python提供了“头文件”和“库文件”,我在C++中调用了头文件中的方法,以此来对Python内容的调用,而这些方法的具体实现在“库文件”中。那么Lua的方式和它一样吗?
在《Lua: getting started》里,官方指出:
To embed Lua into your C or C++ program, you’ll need the Lua headers to compile your program and a Lua library to link with it. If you’re getting a ready-made Lua package for your platform, you’ll probably need the development package as well. Otherwise, just download Lua and add its source directory to your project.
为了让Lua嵌入你的C或C++程序,你需要在编译你的程序时将Lua的头文件添加进工程,然后将Lua的库文件链接进工程。如果你得到了你的平台现成的“Lua包”,你可能还需要“开发包”。或者,就下载Lua源码然后添加进项目就可以了。
我不确定我是否精确理解了官方的意思,我的理解:
- 似乎可以像Python那样以“添加头文件、链接库文件” 这种方式来嵌入。
- 但是这种方式需要一个“开发包”,而这个和“Lua包”是不一样的。我暂时不清楚“开发包”和“Lua包”都特别指什么。。
- 从官方的语气上来看,它们更推荐直接在工程中编译源代码。
从官网下载源码
随后发现主要包括两部分:文档(doc)和源码(src)
而 src 中的头文件/源文件总共只有62个,整个src文件夹不到1MB,看来Lua确实比较“轻量级”。因此我决定使用“直接在工程中编译Lua源代码”的方式。
实践
首先,我将下载的源码src
文件夹拷贝过来加入工程
下面,便是测试调用的Lua脚本文件,以及调用它的C++代码:
(代码上基本参考了《C语言中调用LUA(3)–往Lua脚本中传递变量_Eric_-CSDN博客》)
Lua脚本test.lua
:
--打开文件时的测试打印信息
print("Hello Lua File")
--测试函数
function TestFunction(x,y)
--调用函数时的测试打印信息
print("Call TestFunction")
--返回的运算结果
return x*y
end
其中 TestFunction
是测试的函数,也是我希望在C++中可以调用的函数。
下面是C++代码:
#include<iostream>
#include "LuaSrc/lua.h"
#include "LuaSrc/lualib.h"
#include "LuaSrc/lauxlib.h"
//全局的 Lua state
lua_State* L;
//将Lua函数封装为一个函数
int TestFunction_LuaWrapper(int x, int y)
{
//将“TestFunction”压入栈
lua_getglobal(L, "TestFunction");
//往栈中压入参数
lua_pushnumber(L, x);
lua_pushnumber(L, y);
//调用函数,结果会压入栈
lua_pcall(L
, 2 //nargs:几个参数
, 1 //nresults:几个返回值
, 0); //msgh:If msgh is 0, then the error object returned on the stack is exactly the original error object. Otherwise, msgh is the stack index of a message handler.
//获得结果,就是栈内的第一个元素
int result = (int)lua_tonumber(L, -1);
//出栈
lua_pop(L, 1);
return result;
}
int main(void)
{
//创建一个 Lua state
L = luaL_newstate();
//Opens all standard Lua libraries into the given state.
//对于一个给定的 Lua state 打开所有的Lua标准库
luaL_openlibs(L);
//打开Lua脚本文件
luaL_dofile(L, "test.lua");
//调用函数并打印结果
std::cout << "调用TestFunction(3,7)的结果:" << TestFunction_LuaWrapper(3, 7) << std::endl;
//关闭 Lua state
lua_close(L);
return 0;
}
问题1. 多个Main函数
在最开始,无法通过编译:
1>luac.obj : error LNK2005: _main 已经在 lua.obj 中定义
1>Main.obj : error LNK2005: _main 已经在 lua.obj 中定义
经查看,luac.c
和lua.c
中都已经定义了main函数。
目前我并不知道如何“最正确”处理,于是我暂时将他们的main函数都去掉了。
问题2. 无法找到函数的实现
看来所有的函数都没有找到实现:
1>Main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl lua_close(struct lua_State *)" (?lua_close@@YAXPAUlua_State@@@Z),函数 _main 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_close
1>Main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl lua_settop(struct lua_State *,int)" (?lua_settop@@YAXPAUlua_State@@H@Z),函数 "int __cdecl TestFunction_LuaWrapper(int,int)" (?TestFunction_LuaWrapper@@YAHHH@Z) 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_settop
1>Main.obj : error LNK2019: 无法解析的外部符号 "double __cdecl lua_tonumberx(struct lua_State *,int,int *)" (?lua_tonumberx@@YANPAUlua_State@@HPAH@Z),函数 "int __cdecl TestFunction_LuaWrapper(int,int)" (?TestFunction_LuaWrapper@@YAHHH@Z) 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_tonumberx
1>Main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl lua_pushnumber(struct lua_State *,double)" (?lua_pushnumber@@YAXPAUlua_State@@N@Z),函数 "int __cdecl TestFunction_LuaWrapper(int,int)" (?TestFunction_LuaWrapper@@YAHHH@Z) 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_pushnumber
1>Main.obj : error LNK2019: 无法解析的外部符号 "int __cdecl lua_getglobal(struct lua_State *,char const *)" (?lua_getglobal@@YAHPAUlua_State@@PBD@Z),函数 "int __cdecl TestFunction_LuaWrapper(int,int)" (?TestFunction_LuaWrapper@@YAHHH@Z) 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_getglobal
1>Main.obj : error LNK2019: 无法解析的外部符号 "int __cdecl lua_pcallk(struct lua_State *,int,int,int,int,int (__cdecl*)(struct lua_State *,int,int))" (?lua_pcallk@@YAHPAUlua_State@@HHHHP6AH0HH@Z@Z),函数 "int __cdecl TestFunction_LuaWrapper(int,int)" (?TestFunction_LuaWrapper@@YAHHH@Z) 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _lua_pcallk
1>Main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl luaL_openlibs(struct lua_State *)" (?luaL_openlibs@@YAXPAUlua_State@@@Z),函数 _main 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _luaL_openlibs
1>Main.obj : error LNK2019: 无法解析的外部符号 "int __cdecl luaL_loadfilex(struct lua_State *,char const *,char const *)" (?luaL_loadfilex@@YAHPAUlua_State@@PBD1@Z),函数 _main 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _luaL_loadfilex
1>Main.obj : error LNK2019: 无法解析的外部符号 "struct lua_State * __cdecl luaL_newstate(void)" (?luaL_newstate@@YAPAUlua_State@@XZ),函数 _main 中引用了该符号
1> 已定义且可能匹配的符号上的提示:
1> _luaL_newstate
1>D:\0_WorkSpace\Cpp\TestLua\Debug\TestLua.exe : fatal error LNK1120: 9 个无法解析的外部命令
目前我也不太能说出为何没能找到。但是经《C编程语言嵌入Lua的用法_Study杨的博客-CSDN博客》的提示,加入extern "C"
后可以解决:
最后成功调用:
总结与问题
目前总结出的方式就是:
- 拷贝Lua源代码入工程
- 将
luac.c
和lua.c
中的main函数移除 - 在include Lua的头文件时需要使用
extern "C"
但目前还有疑问:
- 删除
luac.c
和lua.c
中的main函数一定不是最佳的解决方式,更好的解决方式是什么? - 为何必须使用
extern "C"
才可以让那些函数的实现被找到?