C++与lua的结合,LuaBridge的使用及遇到的坑

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++

https://github.com/zfengzhen/lua_tinker_5.2
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值