C++结合lua的方式进行开发主要有以下几点考虑:
1、C++可复用性并不强
2、在业务需求上频繁更新代码驱动用脚本,C++也能做就是比较费劲
3、一般用lua多,lua比较快,比较简单,比较轻量化
4、C语言用得多,C++挂脚本少,因为C++本身也提供了不少强大的库本身用得就非常顺手
在C++嵌入脚本一般有以下方式:
一是系统真正跑在脚本里
二是主逻辑还是C++,业务处理方法用lua控制,也可能联网部分也用lua比如云风实现的库
联网部分,CPU密集型,IO密集型用C++写,控制上的普通代码用其他脚本语言,可以方便调试,方便响应需求,只需要改动一点点代码
C++用lua脚本库很多,本例子用LuaBridge/LuaBridge.h库
luabrige最大的作用是注册C++类给后面的testCallLua使用
//头文件里加入:
#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>
luabridge可以去https://github.com/vinniefalco/LuaBridge下载
创建lua虚拟机:
lua_State* buildLuaEngine(const std::string& file) {
lua_State* L = luaL_newstate();
// need check L
luaL_openlibs(L);
auto ok = reloadLuaScript(L, file);
if(!ok) {
lua_close(L);
L = nullptr;
}
return L;
}
luaL_newstate()工厂类搭建lua虚拟机
luaL_openlibs(L) 加载lua库 ,比如:debug,string io, 数学库,table。。。
reloadLuaScript将lua文件导入到lua虚拟机
lua_close(L) 关闭lua资源
C++与lua的相互调用:
lua调用C++代码相对来说比较简单:
print("hello i'm in lua");
function testA(a)
print("call c++ part");
globalFunction();
a:action();
a:doPrint(1,2);
end
直接通过参数获得C++类或方法即可
C++调用lua代码比较繁琐:
注册部分:
void registerClassAndFucntions(lua_State* L) {
using namespace luabridge;
getGlobalNamespace(L).addFunction("globalFunction", globalFunction);
getGlobalNamespace(L)
.beginClass<A>("A")
.addFunction("action", &A::action)
.addFunction("doPrint", &A::doPrint)
.addFunction("goodMan", &A::goodMan)
.endClass()
.deriveClass<B,A>("B")
.addFunction("hello", &B::hello)
.endClass();
}
通过此方法将C++类及函数注册成lua可用的东西
registerClassAndFucntions:C++里注册lua虚拟机
globalFunction注册全局函数,lua里调用globalFunction就是调用C++的globalFunction
beginClass(“A”)注册类
deriveClass生成派生类
addFunction注册函数
调用部分:
void testCallLua(lua_State* L) {
A a;
// lua_getglobal(L, "testA");
// luabridge::push(L,&a);
// lua_pcall(L, 1, 0, 0);
B b;
lua_getglobal(L, "testAAndB");
luabridge::push(L, &a);
luabridge::push(L, &b);
lua_pcall(L, 2, 0, 0);
}
lua_getglobal(L, “testA”);得到lua全局function testA
luabridge::push(L, &a); 将a作为luafunction 的参数
lua_pcall(L, 1, 0, 0); 1表示需要1个参数(push几个就是几),后面的0表示没有参数返回,再后面0专门处理错误的,具体看lua接口相关文档
在lua里直接调用globalFunction();就可以获取到C++中的全局函数
封装C++调用lua接口:
luabrige用 C++调用lua还是很不方便的,需要二次封装,可以通过模板进行封装
封装了之后调用
LuaScript script;
script.callLuaScript("testAndB", &a, &b)
就行了
部分封装代码:
#ifndef FND_LUA_SCRIPT_H
#define FND_LUA_SCRIPT_H
#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <deque>
#include <functional>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
namespace Yt{
namespace Utility{
class LuaScript {
public:
enum class BacktraceState{
Trace,
Untrace
};
using RegisterFunction = std::function<bool(lua_State*)>;
void init(const std::string& initfile, RegisterFunction func);
bool reload(const std::string& initfile, RegisterFunction func);
bool reload();
~LuaScript();
explicit LuaScript(BacktraceState state, int checkTime,
int singleCallMaxDuration, bool ncheck = false)
:m_isRunning(false),
m_callLuaTimes(0),
m_currentLuaErrorId(0),
m_duration(std::chrono::milliseconds(signleCallMaxDuration)),
m_traceState(state),m_needCheck(ncheck){}
class Exception:public std::runtime_error{
public :
Exception(const std::string& info):std::runtime_error(info.c_str()){}
};
int64_t callLuaTimes() const{ return m_callLuaTimes.load(); }
lua_State* state(){ return m_lua; }
template <class... Param>
void callLuaScript(const std::string& func, Param&&... param) {
if(prepareForLua(func)) {
pushParam(std::forward<Param>(param)...);
luaCallback(func, sizeof...(param));
}
}
template <class... Param>
bool callCondition(bool rev, const std::string& func, Param&&... param) {
if(prepareForLua(func)) {
pushParam(std::forward<Param>(param)...);
return luaCondition(rev, func, sizeof...(param));
}
return rev;
}
//。。。
lua动态更新:
对lua虚拟机进行重新构建,将lua虚拟机的构建代码封装成一个函数,下次调用时就会把新的lua脚本加载进来,一般先新的构造好之后再老的释放掉
lua_State* buildLuaEngine(const std::string& file) {
lua_State* L = luaL_newstate();
// need check L
luaL_openlibs(L);
auto ok = reloadLuaScript(L, file);
if(!ok) {
lua_close(L);
L = nullptr;
}
return L;
}
更多用法参考luabridge文档
makefile文件:
main:main.cpp
g++ -I /usr/include/lua5.2 -std=c++11 -o main main.cpp -llua5.2
linux下头文件一般放在/usr/include中,因为不是通用的查找路径加了个lua5.2文件夹,在makefile使用的时候要加上/usr/include/lua5.2路径,参数-I表示路径,-llua5.2语句链接lua库
lua文件:
print("hello i'm in lua");
function testA(a)
print("call c++ part");
globalFunction();
a:action();
a:doPrint(1,2);
end
function testAAndB(a, b)
print("A and B");
globalFunction();
a:action();
a:doPrint(1,2);
b:hello("Good using c++ and Lua");
end
以下是完整的lua结合c++例子代码:
#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>
#include <iostream>
#include <string>
class A {
public:
void action() { std::cout << "hello I'm A\n";}
virtual void doPrint(int a, int b) {
std::cout << "in A a : " << a << " b : " << b << std::endl;
}
std::string goodMan() const { return "goodman";}
};
class B : public A {
public:
void hello(const std::string& info) const {
std::cout << "hello: " << info << std::endl;
}
virtual void doPrint(int a, int b) override {
std::cout << "in B just " << (a + b) << std::endl;
}
};
void globalFunction() {
std::cout << "hello this is a global func\n";
}
bool reloadLuaScript(lua_State* L, const std::string& luafile) {
int state = luaL_dofile(L, luafile.c_str());
if(state != LUA_OK) {
// std::cout << "ok";
return false;
}
return true;
}
void registerClassAndFucntions(lua_State* L);
void testCallLua(lua_State* L);
lua_State* buildLuaEngine(const std::string& file) {
lua_State* L = luaL_newstate();
// need check L
luaL_openlibs(L);
auto ok = reloadLuaScript(L, file);
if(!ok) {
lua_close(L);
L = nullptr;
}
return L;
}
int main(int argc, char** argv) {
if(argc != 2) return 1;
std::cout << "try load file " << argv[1] << std::endl;
auto L = buildLuaEngine(argv[1]);
if(L) {
registerClassAndFucntions(L);
testCallLua(L);
}
if(L) {
lua_close(L);
L = nullptr;
}
}
void registerClassAndFucntions(lua_State* L) {
using namespace luabridge;
getGlobalNamespace(L).addFunction("globalFunction", globalFunction);
getGlobalNamespace(L)
.beginClass<A>("A")
.addFunction("action", &A::action)
.addFunction("doPrint", &A::doPrint)
.addFunction("goodMan", &A::goodMan)
.endClass()
.deriveClass<B,A>("B")
.addFunction("hello", &B::hello)
.endClass();
}
void testCallLua(lua_State* L) {
A a;
// lua_getglobal(L, "testA");
// luabridge::push(L,&a);
// lua_pcall(L, 1, 0, 0);
B b;
lua_getglobal(L, "testAAndB");
luabridge::push(L, &a);
luabridge::push(L, &b);
lua_pcall(L, 2, 0, 0);
}