先来说下大致脚本引擎框架,此次采用如下,即运行C++代码启动程序,然后加载Lua脚本执行!
1.基础
Lua脚本中只能调用 int (*lua_CFunction) (lua_State *L) 这种类型的C/C++函数;
所有的C/C++函数如果要给Lua进行调用,只能用这样的函数来封装;
那么在C/C++中怎么获得Lua传递过来的参数? 通过操作Lua 虚拟机的栈 !下面我们来了解Lua堆栈!
Lua C/C++互相调用应用:
(1)Lua可以让程序员开发在Lua脚本中调用C/C++函数的接口,这个接口称做LuaGlue函数,因为它们可以在Lua环境中整合C/C++的功能。
(2)Lua API提供了函数让C++代码也可以直接调用Lua函数,还提供了方法可以传递字符和长文字给Lua解释。
综合上面两点所述,C/C++代码和Lua脚本之间的交互是双向的!
2.了解Lua栈
Lua和C/C++语言通信的主要方法是一个Lua的虚拟栈,栈的特点是先进后出。
在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶!
3.Lua常用函数()
4.简单封装1
.h文件
#ifndef __LUA_WRAPPER_H__#define __LUA_WRAPPER_H__extern "C"{#include "lua.h" #include "lauxlib.h" #include "lualib.h" #pragma comment(lib, "liblua.dll.a")}class CLuaWrapper{public: // 初始化 static void init(); // 退出 static void exit(); // 执行 static bool exe_lua_file(const char* fileName); // 注册C++函数到lua static void reg_fun2Lua(const char* funName, int(*c_fun)(lua_State* L));};#endif
.cpp文件
#include "LuaWrapper.h"lua_State* g_lua_state = nullptr;// 获取Lua栈内信息// 获取Lua文件名 行数//static void do_log_message(void(*log)(const char* file_name, int line_num, const char* msg), const char* msg) //{// lua_Debug info;// int depth = 0;// while (lua_getstack(g_lua_state, depth, &info)) {//// lua_getinfo(g_lua_state, "S", &info);// lua_getinfo(g_lua_state, "n", &info);// lua_getinfo(g_lua_state, "l", &info);//// if (info.source[0] == '@') {// log(&info.source[1], info.currentline, msg);// return;// }//// ++depth;// }// if (depth == 0) {// log("trunk", 0, msg);// }//}////void log_print(const char* file_name, int line_num, const char* msg)//{// printf("%s:%d \r\n%s\r\n", file_name, line_num, msg);//}void CLuaWrapper::init(){ g_lua_state = luaL_newstate(); luaL_openlibs(g_lua_state);}void CLuaWrapper::exit(){ if (g_lua_state != nullptr) { lua_close(g_lua_state); g_lua_state = nullptr; }}bool CLuaWrapper::exe_lua_file(const char* fileName){ if (luaL_dofile(g_lua_state, fileName)) { // 发生错误时 //const char* msg = luaL_checkstring(g_lua_state, -1); //if (msg) { // file_name, line_num // do_log_message(log_print, msg); //} return false; } return true;}void CLuaWrapper::reg_fun2Lua(const char* funName, int(*c_fun)(lua_State* L)){ lua_pushcfunction(g_lua_state, c_fun); lua_setglobal(g_lua_state, funName);}
测试小Demo1
(1)新建一个Lua文件,test.lua 具体如下:
print("========Lua脚本=======")--add为C导入Lua函数sum = add(10,20)print("计算结果为:")print(sum)print("========Lua脚本=======")
(2)建立一个控制台程序如下:
#includeusing namespace std;#include "LuaWrapper.h"// 原函数static int add(int x, int y){ return x + y;}// 给Lua调用的函数static int add_tolua(lua_State* L){ printf("此处被Lua调用了!\r\n"); // 此处获取Lua传过来的参数 /* 得到参数个数 */ int n = lua_gettop(L); printf("add函数(Lua脚本内)传参个数:%d个\r\n", n); //int x = 0, y = 0; //if (lua_isnumber(L, -1)) { // y = lua_tonumber(L, -1); //} //if (lua_isnumber(L, -2)) { // x = lua_tonumber(L, -2); //} int y = luaL_checkint(L, -1); int x = luaL_checkint(L, -2); // end // 调用C函数 int sum = add(x, y); // 结果压入Lua栈 lua_pushnumber(L, sum); // end // 返回1个值 return 1; }int main(){ //1.初始化Lua CLuaWrapper::init(); // 注册函数 CLuaWrapper::reg_fun2Lua("add", add_tolua); // 执行Lua脚本 CLuaWrapper::exe_lua_file("test.lua"); // 退出关闭Lua CLuaWrapper::exit(); system("pause"); return 0;}
执行结果如下:
5.简单封装2
---先创建有个CLua类
.h文件
#ifndef __CLUA_H__#define __CLUA_H__struct lua_State;#define LuaGlue extern "C" intextern "C" {// Lua内调用C的函数格式typedef int (*LuaFunctionType)(struct lua_State *pLuaState);};class CLua{public: CLua(); virtual ~CLua(); // ====脚本运行错误时调用m_pErrorHandler函数(该函数被设置才被调用)============ // 运行脚本 高级版本Lua的dofile bool RunScript(const char *pFilename); // 运行命令 高级版本Lua的doString bool RunString(const char *pCommand); // end======================================================================== // 获取错误 const char *GetErrorString(void); // 向Lua添加函数 bool AddFunction(const char *pFunctionName, LuaFunctionType pFunction); // num:索引 // Lua栈内索引为num位置的值是字符串则返回,否则返回默认值NULL const char *GetStringArgument(int num, const char *pDefault=NULL); // num:索引 // Lua栈内索引为num位置的值是数字则返回,否则返回默认值0.0 double GetNumberArgument(int num, double dDefault=0.0); // 压入一个字符进入Lua栈 void PushString(const char *pString); // 压入一个数字进入Lua栈 void PushNumber(double value); // 设置错误处理函数 void SetErrorHandler(void(*pErrHandler)(const char *pError)) {m_pErrorHandler = pErrHandler;} // 获取lua_State lua_State *GetScriptContext(void) {return m_pScriptContext;}private: // lua_State lua_State *m_pScriptContext; // 错误处理函数 void(*m_pErrorHandler)(const char *pError);};#endif
.cpp文件
#include#include #include #include extern "C" {#include #include #include }CLua::CLua(){ m_pErrorHandler = NULL; // 老版本5.0.2==================== // 创建Lua m_pScriptContext = lua_open(); // 加载库文件 luaopen_base(m_pScriptContext); luaopen_io(m_pScriptContext); luaopen_string(m_pScriptContext); luaopen_math(m_pScriptContext); luaopen_debug(m_pScriptContext); luaopen_table(m_pScriptContext); // end // 新版本5.3用以下内容 //m_pScriptContext = luaL_newstate(); //luaL_openlibs(m_pScriptContext); // end}CLua::~CLua(){ // 释放资源 if(m_pScriptContext) lua_close(m_pScriptContext);}static std::string findScript(const char *pFname){ FILE *fTest; char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath( pFname, drive, dir, fname, ext ); std::string strTestFile = (std::string) drive + dir + "Scripts\\" + fname + ".LUB"; fTest = fopen(strTestFile.c_str(), "r"); if(fTest == NULL) { //not that one... strTestFile = (std::string) drive + dir + "Scripts\\" + fname + ".LUA"; fTest = fopen(strTestFile.c_str(), "r"); } if(fTest == NULL) { //not that one... strTestFile = (std::string) drive + dir + fname + ".LUB"; fTest = fopen(strTestFile.c_str(), "r"); } if(fTest == NULL) { //not that one... //not that one... strTestFile = (std::string) drive + dir + fname + ".LUA"; fTest = fopen(strTestFile.c_str(), "r"); } if(fTest != NULL) { fclose(fTest); } return strTestFile;}bool CLua::RunScript(const char *pFname){ std::string strFilename = findScript(pFname); const char *pFilename = strFilename.c_str(); // ==== 新版本用luaL_dofile替代以下2步骤============== if (0 != luaL_loadfile(m_pScriptContext, pFilename)) { if(m_pErrorHandler) { char buf[256]; sprintf(buf, "Lua Error - Script Load\nScript Name:%s\nError Message:%s\n", pFilename, luaL_checkstring(m_pScriptContext, -1)); m_pErrorHandler(buf); } return false; } if (0 != lua_pcall(m_pScriptContext, 0, LUA_MULTRET, 0)) { if(m_pErrorHandler) { char buf[256]; sprintf(buf, "Lua Error - Script Run\nScript Name:%s\nError Message:%s\n", pFilename, luaL_checkstring(m_pScriptContext, -1)); m_pErrorHandler(buf); } return false; } // end return true;}bool CLua::RunString(const char *pCommand){ // ==== 新版本用luaL_doString替代以下2步骤============== if (0 != luaL_loadbuffer(m_pScriptContext, pCommand, strlen(pCommand), NULL)) { if(m_pErrorHandler) { char buf[256]; sprintf(buf, "Lua Error - String Load\nString:%s\nError Message:%s\n", pCommand, luaL_checkstring(m_pScriptContext, -1)); m_pErrorHandler(buf); } return false; } if (0 != lua_pcall(m_pScriptContext, 0, LUA_MULTRET, 0)) { if(m_pErrorHandler) { char buf[256]; sprintf(buf, "Lua Error - String Run\nString:%s\nError Message:%s\n", pCommand, luaL_checkstring(m_pScriptContext, -1)); m_pErrorHandler(buf); } return false; } return true;}const char *CLua::GetErrorString(void){ return luaL_checkstring(m_pScriptContext, -1);}bool CLua::AddFunction(const char *pFunctionName, LuaFunctionType pFunction){ lua_register(m_pScriptContext, pFunctionName, pFunction); return true;}const char *CLua::GetStringArgument(int num, const char *pDefault){ return luaL_optstring(m_pScriptContext, num, pDefault);}double CLua::GetNumberArgument(int num, double dDefault){ return luaL_optnumber(m_pScriptContext, num, dDefault);}void CLua::PushString(const char *pString){ lua_pushstring(m_pScriptContext, pString);}void CLua::PushNumber(double value){ lua_pushnumber(m_pScriptContext, value);}
main测试小Demo
#include#include #include LuaGlue _Version(lua_State *L){ puts("Lua控制台测试版本1.0"); return 0;}char gpCommandBuffer[254];const char *GetCommand(void){ printf("=====> "); return gets_s(gpCommandBuffer, 254); puts("\n");}void main(void){ // 打印说明 puts("========================="); puts("||Lua控制台接收输入命令||"); puts("||请输入Lua脚本,[Q]退出||"); puts("========================="); CLua *pLua = new CLua; pLua->AddFunction("version", _Version); // 处理命令 const char *pCommand = GetCommand(); while(stricmp(pCommand, "Q") != 0) { // 将字符串传递给CLua if(!pLua->RunString(pCommand)) { printf("错误:%s\n", pLua->GetErrorString()); } // 获取下一个命令 pCommand = GetCommand(); } delete pLua;}
测试结果