Lua & C++

# Lua 与 C++ 交互
## 提供系统级别Lua API
提供系统级别API需要对Lua源码进行修改
### Lua源码编译
[LuaResourceCode]:https://github.com/lua/lua "lua源码下载地址"
[LuaResourceCode2]: https://github.com/LuaDist/lua "lua源码下载地址CMake版"
[lua源码下载地址Makefile版][LuaResourceCode]
[lua源码下载Cmake版][LuaResourceCode2]
下载好源码,Linux下编译非常简单,如果要使用VS编译,可以使用CMake版,生成了VS的sln之后打开解决方案,其中:
- liblua: lua动态库项目
- liblua_static: lua静态库项目
- lua: lua解释器
- luac: lua编译器
修改源码我们对动态库进行修改
- loslib.c是Lua os API文件, 接下来对这个文件进行修改,添加一个os API
### 添加lua os API
```c++
static int os_test(lua_State* L) {
print("Test the add lua api. ");
lua_pushboolen(L, 0);
return 1;
}
```
上面代码写了一个简单的os_test函数,其中就是打印一行数据,lua_pushboolen(L, 0)的意思是压栈一个参数为0的bool值,这个压栈的参数就是Lua调用os_test Api的返回值,这里会返回false,最后一行return 1的意思是返回值的个数.
```c++
static const luaL_Reg syslib[] = {
//...
{"test", os_test},
{NULL, NULL}
};
```
在写完os_test之后,lua还并不能直接调用,需要进行注册,在源码中找到syslib这个数组,添加一行{"test", os_test}, 第一个参数表示提供给外部的函数名,第二个参数是一个函数指针, 进行完这一步,编译生成最新的dll,之后再编译lua项目,然后运行lua.exe, 调用我们刚刚写的API
```lua
print(os.test())
```
调用完之后会看到输出"Test the add lua api.", 并且还会打印返回值(false).
## 提供dll来扩展Lua核心功能
```c++
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#pragma comment(lib, "lua.lib")
};
int Add(int a, int b) {
return a + b;
}
static const char* const ERROR_ARGUMENT_COUNT = "参数数目错误!";
void ErrorMsg(lua_State* luaEnv, const char* const pszErrorInfo) {
lua_pushstring(luaEnv, pszErrorInfo);
lua_error(luaEnv);
}
// 检测函数调用参数个数是否正常
void CheckParamCount(lua_State* luaEnv, int paramCount) {
// lua_gettop获取栈中元素个数.
if (lua_gettop(luaEnv) != paramCount) {
ErrorMsg(luaEnv, ERROR_ARGUMENT_COUNT);
}
}
extern "C" __declspec(dllexport)
int Test_Add(lua_State* luaEnv) {
CheckParamCount(luaEnv, 2);
int a = luaL_optinteger(luaEnv, 1);
int b = luaL_optineger(luaEnv, 2);
lua_pushinteger(luaEnv, Add(a, b));
// 一个返回值
return 1;
}
static luaL_Reg lua_libs[] = {
{"Add", Test_Add},
{NULL, NULL}
};
extern "C" __declspec(dllexport)
int luaopen_WinFeature(luaState* luaEnv) {
const char* const library_name = "test";
lua_register(luaEnv, library_name, lua_libs);
return 1;
}
```
导出函数的格式必须为:
```c++
extern "C" int ExportFuncName(luaState* luaEnv);
```
lua_State里面保存了一个属于自己的堆栈信息,取参数和返回参数都是通过压栈或者出栈来操作,luaL_optxxx(出栈), luaL_pushxxx(入栈)
```c++
extern "C" __declspec(dllexport)
int luaopen_WinFeature(luaState* luaEnv) {
const char* const library_name = "test";
lua_register(luaEnv, library_name, lua_libs);
return 1;
}
```
使用dll方式相比添加源码只是多了luaopen_WinFeature这个函数, 这个函数是Lua DLL入口函数,我们通过luaL_register将LIBRARY_NAME对应的库名,以及luaL_Reg数组对应的导出列表来注册到lua_State*对应的Lua环境中。
### Lua使用Dll
lua 搜索目录顺序
- 当前目录
- lua安装目录下的clibs目录
将刚刚编译的DLL放到与lua.exe统级目录下
```lua
require "test"
print(test.Add(10, 20))
```
## C++ 加载Lua文件,调用Lua函数
```lua
// test_add.lua
function add(int a, int b) do
return a + b
end
```
```c++
lua_State* InitLuaEnv() {
lua_State* luaEnv = lua_open();
luaopen_base(luaEnv);
luaL_openlibs(luaEnv);
return luaEnv;
}
bool LoadLuaFile(lua_State* luaEnv, const std::string& fileName) {
int result = luaL_loadfile(luaEnv, fileName.c_str());
return result ? false : true;
}
lua_CFunction GetClobalProc(lua_State* luaEnv, const std::string& procName) {
lua_getglobal(luaEnv, procName.c_str());
if (!lua_iscfunction(luaEnv, 1))
return 0;
return lua_tocfunction(luaEnv, 1);
}
int main(void) {
lua_State* luaEnv = InitLuaEnv();
if (!luaEnv) {
return -1;
}
if (!LoadLuaFile(luaEnv, "./test_add.lua")) {
std::cout << "Load lua file failed!" << std::endl;
return -1;
}
int a = 10, b = 20;
// 将要调用的函数和函数调用参数入栈
lua_getglobal(luaEnv, "add");
lua_pushinteger(luaEnv, a);
lua_pushinteger(luaEnv, b);
// 2代表有二个参数,1表示返回值有一个,0表示发生错误时的处理函数
lua_pcall(luaEnv, 2, 1, 0);
if (lua_isnumber(L, -1))
}
```
原文地址:https://www.cnblogs.com/gd-luojialin/p/15028171.html