C++结合lua的使用

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);

}
发布了152 篇原创文章 · 获赞 35 · 访问量 6858

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104099515