Using C + + with lua

There are the following considerations in the development of C + + in combination with lua:

1. C + + reusability is not strong
2. Frequent update of code driven scripts on business requirements, C + + can also do it is more laborious
3. Generally, there are many Luas, which are faster, simpler and lighter
4. C language is used more, C + + hanging script is less, because C + + itself provides many powerful libraries, which are easy to use

There are generally the following ways to embed scripts in C + +:

One is that the system runs in the script

The second is the main logic or C + +. The business processing method is controlled by lua, or the networking part is also implemented by lua, such as cloud wind library

The networking part, CPU intensive and IO intensive, is written in C + +, and the common code on the control is written in other script languages, which is convenient for debugging and responding to the needs. Only a little code needs to be changed

There are many lua script libraries in C + +. In this example, LuaBridge/LuaBridge.h library is used

The main function of luabrice is to register C + + classes for later testCallLua

//Add:
#include <lua.hpp>
#include <LuaBridge/LuaBridge.h>

Luabridge can be downloaded from https://github.com/vinniefalco/LuaBridge

To create a lua virtual machine:

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

Building lua virtual machine with lual ﹣ newstate() factory class
Lual openlibs (L) loads lua libraries, such as debug,string io, math library, table...
reloadLuaScript imports lua files into lua virtual machine
lua close (L) close lua resources

Mutual calls between C + + and lua:

It is relatively simple for lua to call C + + Code:

 print("hello i'm in lua");
function testA(a)
	print("call c++ part");
	globalFunction();
	a:action();
	a:doPrint(1,2);
end

Directly obtain C + + classes or methods through parameters

Calling lua code in C + + is tedious:
Registration:

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

}

In this way, C + + classes and functions are registered as something available to lua

Register class and functions: register lua virtual machine in C + +
Global function registers global function. Calling global function in lua is to call global function in C + +
beginClass("A") registration class
deriveClass generate derived class
addFunction register function

Call part:

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"); get lua global function testA
Luabridge:: push (L, & A); use a as a parameter of luafunction
lua ﹣ pcall (L, 1, 0, 0); 1 means that 1 parameter is needed (push several is just a few), the following 0 means no parameter is returned, and then the latter 0 deals with errors specially. See the lua interface related documents for details
The global function in C + + can be obtained by directly calling the global function () in lua

Encapsulate C + + to call lua interface:

It's not convenient for lua to be called by luablige in C + +. It needs to be encapsulated twice. It can be encapsulated by template
After encapsulation, call

LuaScript script;
script.callLuaScript("testAndB", &a, &b)

All right.

Partial package code:

#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 dynamic update:

Rebuild the lua virtual machine, encapsulate the building code of the lua virtual machine into a function, load the new lua script in the next call, and generally release the new one after the new one is constructed

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

Refer to the luabridge documentation for more usage

makefile file:

main:main.cpp
	g++ -I /usr/include/lua5.2 -std=c++11 -o main main.cpp -llua5.2

Under linux, the header file is usually placed in / usr/include. Because it is not a general search path, a lua5.2 folder is added. When using makefile, the / usr/include/lua5.2 path is added. The parameter - I indicates the path, and the - llua5.2 statement links the Lua library

lua file:

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

The following is the complete lua combined with c + + example code:

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

}
Published 152 original articles, won praise 35, visited 6852
Private letter follow

Keywords: Makefile less github Linux

Added by phpcoder24july on Tue, 28 Jan 2020 13:07:59 +0200