| ## 提供系统级别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)) |
|
|
|
|
|
|
| } |
| ``` |