## 提供系统级别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.\n"); |
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}, |
|
}; |
``` |
|
在写完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}, |
|
}; |
|
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)) |
|
|
|
|
|
|
} |
``` |