LuaBridge介绍
LuaBridge 是一个简单好用的轻量级且无依赖的库,用于在C ++和 LUA(一种强大,快速,轻量级,可嵌入的脚本语言)之间来回映射数据,函数和类。
github地址:
https://github.com/vinniefalco/LuaBridge https://github.com/kunitoki/LuaBridge3
为什么使用Lua
实现业务的热更新,或者再不改动源码和从新编译的情况下用脚本对业务进行模块化测试,提高不少效率。C++和脚本结合使用是非常好的实践,这种用法提供了非常大的灵活度和自由空间。
脚本文件能够作配置文件和编写复杂的函数。更重要的一点是修改脚本文件后无需重新编译,它帮你提高效率。甚至可以设计这样的一个系统,在不修改源码从新打包部署的情况下,通过修改脚本或者远程下发脚本的方式实现业务的热更新。
LuaBridge环境准备
luaBridge的使用简单,只需要把luaBridge的一堆头文件目录拷贝进项目包含进去使用。
但是需要提前准备好lua.lib,项目打包生成可执行exe时需要链接它。
编译lua.lib的方法:
进入luaBridge的项目源码中的LuaBridge\Tests\Lua文件夹,里面已经包含了lua的源代码,只需要编译为链接库即可。
这里使用cmake和ps脚本编译lua的源码。
附CMakeLists.txt文件内容:
cmake_minimum_required(VERSION 3.12) project(lua VERSION 0.0.1) set(CMAKE_CXX_STANDARD 11) #################### set output directory #################### set(BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../lua-build) set(LIB_DIR ${BUILD_DIR}/Release) set(LIB_FIX) if (CMAKE_BUILD_TYPE MATCHES "Debug") set(LIB_DIR ${BUILD_DIR}/Debug) set(LIB_FIX _d) endif () get_filename_component(ABSOLUTE_PATH ${LIB_DIR} ABSOLUTE) set(LIB_DIR ${ABSOLUTE_PATH}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_PDB_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(LIB_DIR_FIX ${LIB_DIR}/bin) option(USE_VS_BUILD "use visual studio build." OFF) if (USE_VS_BUILD) set(LIB_DIR_FIX ${LIB_DIR}/bin/Debug) endif () #################### set include path #################### set(SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src ) include_directories( ${SRC_PATH} ) add_definitions( ) #################### scan source files #################### foreach (path ${SRC_PATH}) aux_source_directory(${path} SRC_FILES) endforeach () #message(STATUS ${SRC_FILES}) #过滤不相关的源文件 set(FILE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src) list(REMOVE_ITEM SRC_FILES ${FILE_PATH}/lua.c) #################### version config #################### #生成动态库 dll add_library(${PROJECT_NAME} SHARED ${SRC_FILES}) #生成静态库 lib add_library(${PROJECT_NAME}_static STATIC ${SRC_FILES}) #add_executable(${PROJECT_NAME} WIN32 ${SRC_FILES}) #add_executable(${PROJECT_NAME} ${SRC_FILES}) #################### set target properties #################### set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX _d) set_target_properties(${PROJECT_NAME}_static PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) #################### set target dependencies #################### #set(LOGGING_LIB ${LIB_DIR}/lib/Logging${LIB_FIX}.lib) set(THIRD_LIBS #${LOGGING_LIB} ) target_link_libraries(${PROJECT_NAME} PRIVATE ${THIRD_LIBS})
接下来配置vs的编译环境,调用cmake指令完成最终的编译和库的生成。
附ps脚本:
$VcpkgPath = "F:/vcpkg/scripts/buildsystems/vcpkg.cmake" #if (($result = Read-Host "Enter the full path of vcpkg.cmake[default: F:/vcpkg/scripts/buildsystems/vcpkg.cmake]") -eq '') {} else {$VcpkgPath=$result} Write-Host "`n VcpkgPath: $VcpkgPath" -ForegroundColor Yellow Push-Location 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools' cmd /c "vsvars32.bat&set" | ForEach-Object { if ($_ -match "=") { $v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])" } } Pop-Location write-host "`nVisual Studio 2015 Command Prompt variables set." -ForegroundColor Yellow Write-Host "`n build for this module project." -ForegroundColor Green cmake . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_TOOLCHAIN_FILE="$VcpkgPath" -Wno-dev -G "NMake Makefiles" Set-Location build nmake #nmake install Set-Location ..
最终生成的想要的lua.lib文件。
LuaBridge简单示例
新建项目文件夹testlua,把luaBridge的文件夹拷贝进去。
src文件夹中为应用的入口main.cpp
lua文件夹中放上去lua的几个头文件:lualib.h,lua.h,lauxlib.h,luaconfig.h
测试的main.cpp内容如下:
//引用c文件的头文件所以需要加上extern "C" extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } #include "LuaBridge\LuaBridge.h" #include <iostream> class test_lua { public: test_lua() { m_test_string = "c++ test string"; } ~test_lua() { } //test方法 void test(int a,int b) { printf("c++ test function %d+%d=%d\n", a, b, a+b); } //属性set方法 void SetName(std::string name) { m_name = name; } //属性get方法,注意需要后面加const std::string GetName() const { return m_name; } //供lua调用方法,返回多个参数方法 int cFunc(lua_State* L) { //返回参数1 lua_pushstring(L,"str1"); //返回参数1 lua_pushstring(L,"str2"); //返回参数个数 return 2; } std::string m_test_string; std::string m_name; static int m_static_data; }; //test_lua静态变量定义(静态变量在类内只是声明) int test_lua::m_static_data; //test_lua子类 class test_lua_child :public test_lua { public: test_lua_child(std::string test) :m_test_child_string(test) { printf("call test_lua_child constructor\n"); } ~test_lua_child() { } std::string m_test_child_string; }; int main(){ std::cout << "hello test lua begin" << std::endl; //初始化Lua (最后记得调用lua_close(lua_state)释放) lua_State* lua_state = luaL_newstate(); //加载Lua基本库 luaL_openlibs(lua_state); luabridge::getGlobalNamespace(lua_state) .beginNamespace("test") .beginClass<test_lua>("test_lua") .addConstructor<void (*) (void)> ()//无参构造函数的注册 .addData("test_str",&test_lua::m_test_string)//注册变量到lua .addStaticData("static_data", &test_lua::m_static_data)//注册静态变量到lua .addFunction("test", &test_lua::test)//注册test、方法到lua(addStaticFunction静态函数注册也类似) .addProperty("name",&test_lua::GetName,&test_lua::SetName)//属性方法的注册(addStaticProperty静态属性方法也类似) .addCFunction("cFunc",&test_lua::cFunc)//注册返回多个参数给lua的方法 .endClass() .deriveClass<test_lua_child, test_lua> ("test_lua_child")//子类的注册 .addConstructor<void (*) (std::string)> ()//有参构造函数的注册 .addData("test_child_string", &test_lua_child::m_test_child_string)//注册变量到lua .endClass() .endNamespace(); //创建test_lua对象 test_lua test; luabridge::setGlobal(lua_state, &test, "test_lua");//注册test_lua对象到lua //运行lua脚本 luaL_dofile(lua_state, "test.lua"); //关闭Lua lua_close(lua_state); std::cout << "hello test lua end" << std::endl; return 0; }
我们的test.lua文件如下:
--lua 打印lua script print("lua script") --调用成员变量m_test_string(test_str为注册的名字) print(test_lua.test_str) --调用c++静态变量(需要加上test命名空间) test.test_lua.static_data=12 print("static_data: "..test.test_lua.static_data) --调用c++类test_lua属性name test_lua.name="name_property"; print("name: "..test_lua.name); --lua调用c++方法test_lua为c++类在lua的注册名,调用test方法 test_lua:test(3,4) --调用c++调用方法返回多个值 local ret1,ret2 = test_lua:cFunc() print("ret1="..ret1.." ret2="..ret2) --创建test_lua_child对象 local test_lua_child = test.test_lua_child("test_string") --调用其变量 print("child string:"..test_lua_child.test_child_string); --调用父类的name属性 test_lua_child.name="child_name_property"; print("name:"..test_lua_child.name); --lua 方法加法 function lua_add_function(a,b) print("lua_add_function") return a+b; end --lua 方法字符串加法(..是相加语法) function lua_add_str_function(a,b) print("lua_add_str_function") return a..b; end
最后,编译运行一下:
同样使用的cmake编译生成可执行exe。
LuaBridge使用中遇到的坑
不支持引用?不支持引用?不支持引用?
这确实有点儿太坑。要知道引用在c++中可是大量的存在和使用。
不过有些确实支持的,就是const 类型的常量引用。你说这怪不怪?就是只能用,不能修改。
比如:
void printMessage(const std::string& s) { std::cout << s << std::endl; }
这是ok的,可以绑定到lua,且正常运行。
但下面的这个,就不行了,没法使用luaBridge绑定,不信可以试试。
int setEnableStatus(int axis, bool& enable){ enable = true; return 0; }
在github上咨询说是这种的不支持,哎。这种的咋就不支持呢,不美。
给官方提了issue,给出的答复是,没办法,变通下吧,lua本身就不支持引用。
折中的解决办法是从新包装下,比如:
struct RetResult0{ int code; bool enable; }; RetResult0 setEnableStatus(int axis){ RetResult0 ret; ret.enable = true; ret.code = 0; return ret; }
这种的是能解决,但是就是看起来很丑,且,有几个这这样的就需要包装几个。谁有更好的办法吗?
附上CMakeLists.txt
cmake_minimum_required(VERSION 3.12) project(main VERSION 0.0.1) set(CMAKE_CXX_STANDARD 11) #################### set output directory #################### set(BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../main-build) set(LIB_DIR ${BUILD_DIR}/Release) set(LIB_FIX) if (CMAKE_BUILD_TYPE MATCHES "Debug") set(LIB_DIR ${BUILD_DIR}/Debug) set(LIB_FIX _d) endif () get_filename_component(ABSOLUTE_PATH ${LIB_DIR} ABSOLUTE) set(LIB_DIR ${ABSOLUTE_PATH}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_PDB_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIB_DIR}/lib) set(LIB_DIR_FIX ${LIB_DIR}/bin) option(USE_VS_BUILD "use visual studio build." OFF) if (USE_VS_BUILD) set(LIB_DIR_FIX ${LIB_DIR}/bin/Debug) endif () #################### set include path #################### set(SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src ) include_directories( ${SRC_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/ ${CMAKE_CURRENT_SOURCE_DIR}/lua ) add_definitions( ) #################### scan source files #################### foreach (path ${SRC_PATH}) aux_source_directory(${path} SRC_FILES) endforeach () #message(STATUS ${SRC_FILES}) #过滤不相关的源文件 #################### version config #################### add_executable(${PROJECT_NAME} ${SRC_FILES}) #################### set target properties #################### set_target_properties(${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX _d) #################### set target dependencies #################### set(LUA_LIB ${LIB_DIR}/lib/lua${LIB_FIX}.lib) set(THIRD_LIBS ${LUA_LIB} ) target_link_libraries(${PROJECT_NAME} PRIVATE ${THIRD_LIBS})
引用
C++和Lua交互教程(基于LuaBridge)_CSDN云计算的博客-CSDN博客_luabridge
C++反射:全方位解读Lura库的前世今生! - 云+社区 - 腾讯云
https://github.com/zfengzhen/lua_tinker_5.2
tolua++ 编译 及使用 简单介绍_乌班图ysm的博客-CSDN博客_tolua++