工程介绍
通过《C++ 与 Lua 交互 使用模板特化封装》,已经对 C++ 与 Lua 交互有了了解。
现在的这套 C++ 与 Lua 相互调用的框架,是之前单位项目中提取出来的,在已上线的端游中使用。
这里使用 lua 5.1.5、tolua++ 1.0.92、boost 1.68.0、zlib。
开发环境:vs 2017 、lua 调试插件 BabeLua。
弊端就是,使用的是 Lua 5.1 老版本,由于 tolua++ 的原因 ...
先贴图,介绍一下工程目录。

•lua 工程;lua代码 —— 编译lua.lib•tolua 工程:tolua++代码 —— 编译 tolua.lib 和 tolua.exe•LuaProject 工程:lua和c++相互交互工程。•LuaScript 工程:插件BabeLua 创建,lua脚本编写、调试使用。
lua 工程
lua 工程,是 lua 静态链接库工程。
已经在《C++ 与 Lua 交互 使用模板特化封装》中,讲过了,这里略过。
tolua 工程
创建空工程、添加tolua代码文件。
这里注意的是 tolua\src 目录下有 bin 和 lib 路径代码。分别控制编译 exe 和 lib文件。
我这里为了方便,将bin和lib代码同时加入tolua工程。
然后控制 配置类型来生成 应用程序(.exe) 和 静态库(.lib)
修改工程配置
附加包含目录:..\include;....\lua\src;
附加依赖项:lua.lib
预处理器定义:_CRT_SECURE_NO_WARNINGS
编译生成 tolua.lib 和 tolua.exe
LuaProject 工程
看一下工程代码如图:

还挺多,不要慌,这里主要实现的读取的文件可以是zip文件(压缩以后的lua文件)。
主要类也就是 ScriptVmArgs类 和 ScriptVM类 来实现的 lua与c++ 的交互。而且读取文件也提供了一个已经剥离的zip文件的函数。
之前使用的是模板封装,这里使用的是递归+元组封装。
这里贴出代码如下:
ScriptVmArgs.h
/*************************************************vic.MINg 2018/09/18************************************************/#pragma once#include <string>// "boost/config/user.hpp" 取消 BOOST_ALL_NO_LIB 注释#define BOOST_ALL_NO_LIB#include "boost/tuple/tuple.hpp"struct lua_State;class ScriptVmArgs{friend class ScriptVM;class Args{public:virtual int Size()const = 0;};class ArgsInput:public Args{public:virtual void Push(lua_State& lua)const = 0;};class ArgsOutput:public Args{public:virtual void Pop(lua_State& lua) = 0;};template<typename T>class ArgsInputImp:public ArgsInput{public:ArgsInputImp(const T& args) :m_args(args) {}virtual void Push(lua_State& lua)const{Push(lua, m_args);}virtual int Size()const{return boost::tuples::length<T>::value;}private:static void Push(lua_State& lua, const boost::tuples::null_type& args) {}template<typename Head, typename Tail>static void Push(lua_State& lua, const boost::tuples::cons<Head, Tail>& args){// 这里巧妙的使用了 boost::tuple 的 args.get_head() 和 args.get_tail() 方法,两行代码实现了 自身的递归调用。DoPush(lua, args.get_head());Push(lua, args.get_tail());}const T& m_args;};template<typename T>class ArgsOutputImp:public ArgsOutput{public:ArgsOutputImp(T& args) :m_args(args) {}virtual void Pop(lua_State& lua){Pop(lua, m_args, 0);}virtual int Size()const{return boost::tuples::length<T>::value;}private:static void Pop(lua_State& lua, const boost::tuples::null_type& args, int idx) {}template<typename Head, typename Tail>static void Pop(lua_State& lua, const boost::tuples::cons<Head, Tail>& args, int idx){//if (args.get_head())DoPop(lua, args.get_head(), idx - boost::tuples::length<T>::value);Pop(lua, args.get_tail(), idx + 1);}T& m_args;};static void DoPush(lua_State& lua, const char* pcArg);static void DoPush(lua_State& lua, bool bArg);static void DoPush(lua_State& lua, float fArg);static void DoPush(lua_State& lua, int nArg);static void DoPush(lua_State& lua, unsigned uArg);static void DoPop(lua_State& lua, std::string& sArg, int idx);static void DoPop(lua_State& lua, bool& bArg, int idx);static void DoPop(lua_State& lua, float& fArg, int idx);static void DoPop(lua_State& lua, int& nArg, int idx);static void DoPop(lua_State& lua, unsigned& uArg, int idx);};
ScriptVM.h
/*************************************************vic.MINg 2018/09/18************************************************/#pragma once#include <vector>#include <map>#include <string>#include "ScriptVmArgs.h"struct lua_State;struct lua_Debug;typedef void(*lua_Hook) (lua_State *L, lua_Debug *ar);typedef int(*lua_CFunction) (lua_State *L);class ScriptVM{public:ScriptVM(void);virtual ~ScriptVM(void);bool Init(void);void Destroy(void);void ExecuteScriptFile(const char * sScriptFileName, bool bForceReload = false, bool bAssertOnError = true);void ExecuteScript(const char * sScript, bool bAssertOnError = true);bool ExecuteScriptFunc(const std::vector<const char *>& modules, const char * func, bool bAllowNonexist, const char * sig = "", ...);template<typename InTuple, typename OutTuple>void ExecuteFunc(const char* func, bool bAllowNonexist, const InTuple& inTuple, OutTuple& outTuple, const std::vector<const char*>* pModuls = 0);void ExposeGlobalUserdata(void * va, const char * name, const char * type);void * GetGlobalUserdata(const char * name, const char * verify_type = NULL);void * GetUserdata(const std::vector<const char *>& modules, const char * name, const char * verify_type = NULL);double GetGlobalNumber(const char * name);// 是否存在某变量bool ExistVariable(const std::vector<const char *>& modules, const char * name);// 得到某个Lua变量bool GetNumber(const std::vector<const char *>& modules, const char * name, double& dValue);//void OnEndofUpdate(); //call back function on end of game updatevoid * CreateObjectByTypeName(const char * sTypeName);//由tolua注册过的类名创建对象double GetGlobalTableNumber(const char *sTableName, const char* key);//Wang Hongliang:double ComputingFormula(const char* sFormula, const std::map<std::string, double>& kParams); // 计算数学公式bool ComparingCondition(const char* sCondition, const std::map<std::string, int>& kParams); // 判断条件语句lua_State * GetLuaState() { return m_pLua; }// 设置脚本钩子,只在调试时可用。void SetHook(lua_Hook func, int nMask, int nCount);void RegistGlobalFunction(const char* sFunctionName, lua_CFunction pFunction);// 这里通过 自己写的io流来处理的 文件读写,把文件内容读到了buff 中// 我这里支撑压缩文件的读写,你可以自己实现一下文件读写方法 重写该方法即可static bool LoadFileToBuffer_IO(const char * filename, unsigned char *& buff, int& size, bool bAssertOnError);// 我实现了一下文件读写方法,剥离了压缩文件的读写static bool LoadFileToBuffer(const char * filename, char *& buff, int& size, bool bAssertOnError);std::string GetScriptReturnString(std::string sScript);//static sigslot::signal1<const char*> LuaPrinted;// 获取脚本当前所占用的内存int GetConsumeMemoryCount();// 打印所有全局变量void PrintGlobalVariables();protected:void DoExecuteFunc(const char* func, bool bAllowNonexist, const ScriptVmArgs::ArgsInput& input, ScriptVmArgs::ArgsOutput& output, const std::vector<const char*>* pModuls);lua_State* m_pLua;};template<typename InTuple, typename OutTuple>inline void ScriptVM::ExecuteFunc(const char* func, bool bAllowNonexist, const InTuple& inTuple, OutTuple& outTuple, const std::vector<const char*>* pModuls /* = 0 */){// Pass as ArgsInput and ArgsOutput, not ArgsInputImp and ArgsOutputImp. So that the body of DoExecuteFunc() can be placed in .cpp rather than .h.DoExecuteFunc(func,bAllowNonexist,ScriptVmArgs::ArgsInputImp<InTuple>(inTuple),(ScriptVmArgs::ArgsOutput&)ScriptVmArgs::ArgsOutputImp<OutTuple>(outTuple), // 必须强制转换一下 ScriptVmArgs::ArgsOutput& 上面的代码就不用转换pModuls);}
ScriptVM.cpp
#include "ScriptVM.h"#include "IOServer.h"#include "FileStream.h"extern "C"{#include "lua.h"#include "lualib.h"#include "lauxlib.h"}#include "tolua++.h"#include<stdio.h>#include <assert.h>#include <sstream>#include <iostream>#include <fstream>#define DEBUG_STACK 0//end of tolua bind declareextern void tolua_open_binding(lua_State * pLua);static void stackDump(lua_State *pLua);//sigslot::signal1<const char*> ScriptVM::LuaPrinted;//error_msg handling functionstatic void error_msg(bool bUseAssert, const char * pacFmt, ...){//#if defined(_DEBUG)char acTemp[2048];va_list args;va_start(args, pacFmt);vsprintf_s(acTemp, pacFmt, args);va_end(args);if (bUseAssert){assert( acTemp);}//#endif}void report_last_error(lua_State *pLua, bool bUseAssert){lua_getglobal(pLua, "_ALERT");error_msg(bUseAssert, "%s\n", lua_tostring(pLua, -2));error_msg(bUseAssert, "%s\n", lua_tostring(pLua, -1));lua_pop(pLua, 2); /* remove error_msg message and _ALERT */}std::string & std_string_format(std::string & _str, const char * _Format, ...) {std::string tmp;va_list marker = NULL;va_start(marker, _Format);size_t num_of_chars = _vscprintf(_Format, marker);if (num_of_chars > tmp.capacity()) {tmp.resize(num_of_chars + 1);}vsprintf_s((char *)tmp.data(), tmp.capacity(), _Format, marker);va_end(marker);_str = tmp.c_str();return _str;}ScriptVM::ScriptVM(void){}ScriptVM::~ScriptVM(void){}static int PrintStringList(lua_State * pLua) {int n = lua_gettop(pLua); /* number of arguments */int i;lua_getglobal(pLua, "tostring");std::string out;for (i = 1; i <= n; i++) {const char *s;lua_pushvalue(pLua, -1); /* function to be called */lua_pushvalue(pLua, i); /* value to print */lua_call(pLua, 1, 1);s = lua_tostring(pLua, -1); /* get result */if (s == NULL)return luaL_error(pLua, "`tostring' must return a string to `print'");if (i > 1){out += "\t";out += s;}elseout += s;lua_pop(pLua, 1); /* pop result */}out += "\n";return 0;}bool ScriptVM::Init(void){m_pLua = luaL_newstate();lua_cpcall(m_pLua, luaopen_base, 0);lua_cpcall(m_pLua, luaopen_io, 0);lua_cpcall(m_pLua, luaopen_string, 0);lua_cpcall(m_pLua, luaopen_table, 0);lua_cpcall(m_pLua, luaopen_math, 0);lua_cpcall(m_pLua, luaopen_debug, 0);lua_cpcall(m_pLua, luaopen_os, 0);lua_cpcall(m_pLua, luaopen_package, 0);//luaopen_base(m_pLua);//luaopen_io(m_pLua);//luaopen_table(m_pLua);//luaopen_math(m_pLua);//luaopen_string(m_pLua);//luaopen_debug(m_pLua);//tolua_open_binding(m_pLua);//lua_setgcthreshold(m_pLua, 200); //200k garbage collection thresholdlua_register(m_pLua, "print", PrintStringList);//lua_register(m_pLua, "dofile", OverrideDofile);return true;}void ScriptVM::Destroy(void){lua_close(m_pLua);}void ScriptVM::ExecuteScriptFile(const char * sScriptName, bool bForceReload /* = false*/, bool bAssertOnError /*= true*/){int nSize1 = lua_gettop(m_pLua);//get chunk name as modified script namestd::string sChunkName(sScriptName);for (unsigned int i = 0; i < sChunkName.length(); i++){if (sChunkName[i] == '/' || sChunkName[i] == '.')sChunkName[i] = '_';}//get the chunk globallua_getglobal(m_pLua, sChunkName.c_str());if (bForceReload || !lua_isfunction(m_pLua, -1))//if force reload or not found{//load it firstchar * pBuff;int nSize;if (LoadFileToBuffer(sScriptName, pBuff, nSize, bAssertOnError)){luaL_loadbuffer(m_pLua, (char *)pBuff, nSize, sScriptName);delete[] pBuff;// luaL_loadfile(m_pLua, sScriptName);lua_setglobal(m_pLua, sChunkName.c_str());lua_getglobal(m_pLua, sChunkName.c_str());}elsegoto failed;}if (lua_pcall(m_pLua, 0, 0, 0) != 0){error_msg(bAssertOnError, "error executing script file %s: ", sScriptName);report_last_error(m_pLua, bAssertOnError);}failed:lua_settop(m_pLua, nSize1);}void ScriptVM::ExecuteScript(const char * sScript, bool bAssertOnError){int status = luaL_loadbuffer(m_pLua, sScript, strlen(sScript), sScript);if (status){report_last_error(m_pLua, bAssertOnError);}else{status = lua_pcall(m_pLua, 0, LUA_MULTRET, 0); /* call main */if (status)report_last_error(m_pLua, bAssertOnError);}}/** Execute Script Function func in the script. copy/pasted from the book "programming in LUA"*/bool ScriptVM::ExecuteScriptFunc(const std::vector<const char *>&modules, const char * func, bool bAllowNonexist, const char * sig, ...){bool bIsSuccess = false;//PROFILE("ExecuteScriptFunc");int nSize1 = lua_gettop(m_pLua);//debug#if DEBUG_STACKprintf("debug lua: stack size before ExecuteScriptFunc = %d\n", nSize1);#endifva_list vl;int narg, nres; /* number of arguments and results */va_start(vl, sig);//get the actual functionif (modules.empty()) //func is global{lua_getglobal(m_pLua, func);if (!lua_isfunction(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);goto failed;}}else{//trace down the modulesstd::vector<const char *>::const_iterator it = modules.begin();//get the global module name or the actual function name if there is no modulelua_getglobal(m_pLua, *it);if (!lua_istable(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);goto failed;}for (++it; it != modules.end(); ++it){lua_pushstring(m_pLua, *it);lua_gettable(m_pLua, -2);if (!lua_istable(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);goto failed;}}//get the funclua_pushstring(m_pLua, func);lua_gettable(m_pLua, -2);if (!lua_isfunction(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);goto failed;}}/* push arguments */narg = 0;while (*sig) { /* push arguments */switch (*sig++) {case 'd': /* double argument */case 'f': /* float argument */ // NieXu: Treat float as double, same as printf()lua_pushnumber(m_pLua, va_arg(vl, double));break;case 'i': /* int argument */lua_pushnumber(m_pLua, va_arg(vl, int));break;case 's': /* string argument */lua_pushstring(m_pLua, va_arg(vl, char *));break;case 'b': /* boolean argument */lua_pushboolean(m_pLua, va_arg(vl, bool));break;case 'u': /* light user data */lua_pushlightuserdata(m_pLua, va_arg(vl, void *));break;case 't': /* type user data */{void* pData = va_arg(vl, void *);const char* sType = va_arg(vl, const char*);tolua_pushusertype(m_pLua, pData, sType);break;}case '>':goto endwhile;default:error_msg(true, "invalid option (%c)\n", *(sig - 1));goto failed;}narg++;luaL_checkstack(m_pLua, 1, "too many arguments");}endwhile:/* do the call */nres = strlen(sig); /* number of expected results */if (lua_pcall(m_pLua, narg, nres, 0) != 0) /* do the call */{report_last_error(m_pLua, true);goto failed;}/* retrieve results */nres = -nres; /* stack index of first result */while (*sig){ /* get results */switch (*sig++){case 'd': /* double result */if (!lua_isnumber(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, double *) = lua_tonumber(m_pLua, nres);break;case 'f': /* float result */if (!lua_isnumber(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, float*) = (float)lua_tonumber(m_pLua, nres);break;case 'i': /* int result */if (!lua_isnumber(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, int *) = (int)lua_tonumber(m_pLua, nres);break;case 's': /* string result */if (!lua_isstring(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, std::string*) = lua_tostring(m_pLua, nres);break;case 'b': /* boolean argument */if (!lua_isboolean(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, bool *) = (0 != lua_toboolean(m_pLua, nres));break;case 'u': /* light user data */if (!lua_isuserdata(m_pLua, nres))error_msg(true, "wrong result type,function name: %s\n", func);*va_arg(vl, void **) = lua_touserdata(m_pLua, nres);break;default:error_msg(true, "invalid option (%c)\n", *(sig - 1));}nres++;}bIsSuccess = true;failed:va_end(vl);//clear the stacklua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugint nSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after ExecuteScriptFunc = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endifreturn bIsSuccess;}void ScriptVM::ExposeGlobalUserdata(void * va, const char * name, const char * type){int nSize1 = lua_gettop(m_pLua);#if DEBUG_STACK//debugprintf("debug lua: stack size before ExposeGlobalUserdata = %d\n", nSize1);#endiftolua_pushusertype(m_pLua, va, type);lua_setglobal(m_pLua, name);//clear the stacklua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugint nSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after ExposeGlobalUserdata = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endif}void * ScriptVM::GetGlobalUserdata(const char * name, const char * verify_type /*= NULL*/){void * pRet = NULL;int nSize1 = lua_gettop(m_pLua);int nSize2 = 0;#if DEBUG_STACK//debugprintf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);#endiflua_getglobal(m_pLua, name);//verify typeif (verify_type){tolua_Error tolua_err;if (!tolua_isusertype(m_pLua, 1, verify_type, 0, &tolua_err) ||!tolua_isnoobj(m_pLua, 2, &tolua_err)){tolua_error(m_pLua, "#ferror in function 'ScriptVM::GetGlobalUserdata'.", &tolua_err);goto failed;}}pRet = tolua_tousertype(m_pLua, -1, 0);//clear the stacklua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugnSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after GetGlobalUserdata = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endifreturn pRet;failed://lua_settop(m_pLua,0);lua_settop(m_pLua, nSize1);return NULL;}double ScriptVM::GetGlobalNumber(const char * name){int nSize1 = lua_gettop(m_pLua);#if DEBUG_STACK//debugprintf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);#endiflua_getglobal(m_pLua, name);double ret = tolua_tonumber(m_pLua, -1, 0);//clear the stacklua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugint nSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after GetGlobalUserdata = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endifreturn ret;}void * ScriptVM::GetUserdata(const std::vector<const char *>& modules, const char * name, const char * verify_type/*= NULL*/){void * pRet = NULL;int nSize1 = lua_gettop(m_pLua);int nSize2 = 0;#if DEBUG_STACKprintf("debug lua: stack size before GetUserdata = %d\n", nSize1);#endifif (modules.empty()) //userdata is global{lua_getglobal(m_pLua, name);}else{//trace down the modulesstd::vector<const char *>::const_iterator it = modules.begin();//get the global module name or the actual function name if there is no modulelua_getglobal(m_pLua, *it);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);goto failed;}for (++it; it != modules.end(); ++it){lua_pushstring(m_pLua, *it);lua_gettable(m_pLua, -2);if (!lua_istable(m_pLua, -1)){std::vector<const char *>::const_iterator itMsg = modules.begin();std::string sMsg;for (; itMsg <= it; ++itMsg){sMsg.append(*itMsg);if (itMsg != it){sMsg.append(".");}}error_msg(true, "GetUserdata: Invalid table name: %s\n", sMsg.c_str());goto failed;}}//get the datalua_pushstring(m_pLua, name);lua_gettable(m_pLua, -2);}//verify type//if(verify_type)//{// tolua_Error tolua_err;// if (// !tolua_isusertype(m_pLua,1,verify_type,0,&tolua_err) ||// !tolua_isnoobj(m_pLua,2,&tolua_err)// )// {// error_msg(m_pLua,"#ferror in function 'ScriptVM:GetUserdata: %s\n", name);// goto failed;// }//}pRet = tolua_tousertype(m_pLua, -1, 0);//clear the stacklua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugnSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after GetUserdata = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endifreturn pRet;failed:lua_settop(m_pLua, nSize1);return NULL;}static void stackDump(lua_State *m_pLua) {int i;int top = lua_gettop(m_pLua);for (i = 1; i <= top; i++) { /* repeat for each level */int t = lua_type(m_pLua, i);switch (t) {case LUA_TSTRING: /* strings */printf("`%s'", lua_tostring(m_pLua, i));break;case LUA_TBOOLEAN: /* booleans */printf(lua_toboolean(m_pLua, i) ? "true" : "false");break;case LUA_TNUMBER: /* numbers */printf("%g", lua_tonumber(m_pLua, i));break;default: /* other values */printf("%s", lua_typename(m_pLua, t));break;}printf(" "); /* put a separator */}printf("\n"); /* end the listing */}bool ScriptVM::LoadFileToBuffer(const char * filename, char *& buff, int& size, bool bAssertOnError){std::ifstream ifs(filename, std::ifstream::binary);// get pointer to associated buffer objectstd::filebuf* pbuf = ifs.rdbuf();if (pbuf == nullptr)return false;// get file size using buffer's memberssize = pbuf->pubseekoff(0, ifs.end, ifs.in);pbuf->pubseekpos(0, ifs.in);if (size <= 0)return false;// get file databuff = new char[size];pbuf->sgetn(buff, size);ifs.close();return true;}bool ScriptVM::LoadFileToBuffer_IO(const char * filename, unsigned char *& buff, int& size, bool bAssertOnError){IO::Stream* pStream = IO::IOServer::Instance()->CreateReadStream(filename);if (pStream != NULL && pStream->Open()){size = pStream->GetSize();if (size == 0){std::string sMsg;sMsg.append(filename).append("以上文件中无内容!");assert( sMsg.c_str());}buff = new unsigned char[size];pStream->Read(buff, size);pStream->Close();IO::IOServer::Instance()->ReleaseStream(pStream);return true;}else{std::string sMsg;sMsg.append("[").append(filename).append("]脚本文件不存在!");assert( sMsg.c_str());if (NULL != pStream){IO::IOServer::Instance()->ReleaseStream(pStream);}return false;}}//由tolua注册过的类名创建对象void * ScriptVM::CreateObjectByTypeName(const char * sTypeName){//处理lua脚本int nSize = (int)strlen(sTypeName) + 20;unsigned char* buffer = new unsigned char[nSize];sprintf_s((char*)buffer, nSize, "pMyCreatedObj=%s:new()", sTypeName);//执行脚本ExecuteScript((char*)buffer);void* ret = GetGlobalUserdata("pMyCreatedObj");return ret;}//获得全局表中常量double ScriptVM::GetGlobalTableNumber(const char *sTableName, const char* key){double ret = 0;int nSize1 = lua_gettop(m_pLua);int nSize2 = 0;#if DEBUG_STACK//debugprintf("debug lua: stack size before GetGlobalUserdata = %d\n", nSize1);#endiflua_getglobal(m_pLua, sTableName);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetGlobalTableNumber: %s isn't a Lua Table.", sTableName);goto failed;}lua_pushstring(m_pLua, key);lua_gettable(m_pLua, -2);if (!lua_isnumber(m_pLua, -1)){error_msg(true, "GetGlobalTableNumber: %s isn't a number.", key);goto failed;}ret = lua_tonumber(m_pLua, -1);lua_settop(m_pLua, nSize1);#if DEBUG_STACK//debugnSize2 = lua_gettop(m_pLua);printf("debug lua: stack size after GetUserdata = %d\n", nSize2);if (nSize1 != nSize2)stackDump(m_pLua);#endifreturn ret;failed:lua_settop(m_pLua, nSize1);return 0;}void ScriptVM::SetHook(lua_Hook func, int nMask, int nCount){if (!m_pLua)return;//lua_sethook(m_pLua, func, nMask, nCount);}bool ScriptVM::ExistVariable(const std::vector<const char *>& modules, const char * name){int nSize1 = lua_gettop(m_pLua);if (modules.empty()) //userdata is global{lua_getglobal(m_pLua, name);}else{//trace down the modulesstd::vector<const char *>::const_iterator it = modules.begin();//get the global module name or the actual function name if there is no modulelua_getglobal(m_pLua, *it);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);goto failed;}for (++it; it != modules.end(); ++it){lua_pushstring(m_pLua, *it);lua_gettable(m_pLua, -2);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);goto failed;}}//get the datalua_pushstring(m_pLua, name);lua_gettable(m_pLua, -2);}if (lua_isnil(m_pLua, -1)){goto failed;}else{goto success;}failed:lua_settop(m_pLua, nSize1);return false;success:lua_settop(m_pLua, nSize1);return true;}// 替换字符串void string_replace(std::string &strBig, const std::string &strsrc, const std::string &strdst){std::string::size_type pos = 0;std::string::size_type srclen = strsrc.size();std::string::size_type dstlen = strdst.size();while ((pos = strBig.find(strsrc, pos)) != std::string::npos){strBig.replace(pos, srclen, strdst);pos += dstlen;}}double ScriptVM::ComputingFormula(const char* sFormula, const std::map<std::string, double>& kParams){std::string sRealFormula = sFormula;if (sRealFormula == ""){assert(false && "公式错误");return 0.0;}std::string sParamValue = "0";std::stringstream kStrStream;// 替换参数for (std::map<std::string, double>::const_iterator it(kParams.begin()); it != kParams.end(); it++){// 得到公式// kStrStream<<it->second;// sParamValue = kStrStream.str();sParamValue = std_string_format(sParamValue, "%f", it->second);string_replace(sRealFormula, it->first, sParamValue);/*// 支持在公式中包含的脚本函数调用可以有字符串参数,避免以当前属性名为字符串的参数被属性值替换 [2/18/2011 shuaiwang]if (sRealFormula.find('\'') != 0xffffffff){std::string sProParam = std::string("\'").append(it->first).append("\'");sRealFormula.replace(sProParam, "????");sRealFormula.replace(it->first, sParamValue);sProParam = std::string("\"").append(it->first).append("\"");sRealFormula.replace("????", sProParam);}else{sRealFormula.replace(it->first, sParamValue);}*///sRealFormula.Replace(it->first, sParamValue);}std::string sReturn = "return ";if (sRealFormula.find(sReturn) == std::string::npos){sRealFormula = sReturn.append(sRealFormula);}/*std::stringstream kFunStream;kFunStream<<"computing_formula_teamfunction = function()\n"<< sRealFormula << "\n"<< "end";*/std::string sTempFunction = std::string("computing_formula_teamfunction = function()\n").append(sRealFormula).append("\nend")/*kFunStream.str()*/;ExecuteScript(sTempFunction.c_str(), true);std::vector<const char *> modules;double dValue = 0.0;if (ExecuteScriptFunc(modules, "computing_formula_teamfunction", false, ">d", &dValue) == false){//_logError2("computing_formula_teamfunction这个问题的来源是:", sFormula << ",请在数据表中修改相应公式!");}ExecuteScript("computing_formula_teamfunction = nil", true);return dValue;}bool ScriptVM::ComparingCondition(const char* sCondition, const std::map<std::string, int>& kParams){std::string sRealCondition = sCondition;if (sRealCondition == ""){assert(!"[ScriptVM::ComparingCondition] Condition Error");return false;}std::string sParamValue = "false";for (std::map<std::string, int>::const_iterator it(kParams.begin()); it != kParams.end(); it++){sParamValue = std_string_format(sParamValue, "%d", it->second);string_replace(sRealCondition, it->first, sParamValue);}std::string sReturn = "return ";//sRealCondition = "({true} and {true})[1]";if (sRealCondition.find(sReturn) == std::string::npos){sRealCondition = sReturn.append(sRealCondition);}std::string sTempFunction = std::string("comparing_condition_function = function()\n").append(sRealCondition).append("\nend")/*kFunStream.str()*/;//boost::timer btimer;ExecuteScript(sTempFunction.c_str(), true);std::vector<const char *> modules;bool bValue = false;if (ExecuteScriptFunc(modules, "comparing_condition_function", false, ">b", &bValue) == false){assert(!"[ScriptVM::ComparingCondition] ExecuteScriptFunc Error");}ExecuteScript("computing_formula_function = nil", true);return bValue;}std::string ScriptVM::GetScriptReturnString(std::string sScript){std::string sReturn = "return ";if (sScript.find(sReturn) == 0xffffffff){sScript = sReturn.append("\"").append(sScript).append("\"");}std::string sTempFunction = std::string("string_function = function()\n").append(sScript).append("\nend");ExecuteScript(sTempFunction.c_str(), true);std::vector<const char *> modules;std::string sReturnString;ExecuteScriptFunc(modules, "string_function", false, ">s", &sReturnString);ExecuteScript("string_function = nil", true);return sReturnString.c_str();}void ScriptVM::RegistGlobalFunction(const char* sFunctionName, lua_CFunction pFunction){lua_register(m_pLua, sFunctionName, pFunction);}bool ScriptVM::GetNumber(const std::vector<const char *>& modules, const char * name, double& dValue){int nSize1 = lua_gettop(m_pLua);if (modules.empty()) //userdata is global{lua_getglobal(m_pLua, name);}else{//trace down the modulesstd::vector<const char *>::const_iterator it = modules.begin();//get the global module name or the actual function name if there is no modulelua_getglobal(m_pLua, *it);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);lua_settop(m_pLua, nSize1);return false;}for (++it; it != modules.end(); ++it){lua_pushstring(m_pLua, *it);lua_gettable(m_pLua, -2);if (!lua_istable(m_pLua, -1)){error_msg(true, "GetUserdata: Invalid table name: %s\n", *it);lua_settop(m_pLua, nSize1);return false;}}//get the datalua_pushstring(m_pLua, name);lua_gettable(m_pLua, -2);}if (lua_isnumber(m_pLua, -1)){dValue = tolua_tonumber(m_pLua, -1, 0);lua_settop(m_pLua, nSize1);return true;}else{lua_settop(m_pLua, nSize1);return false;}}void ScriptVM::DoExecuteFunc(const char* func, bool bAllowNonexist, const ScriptVmArgs::ArgsInput& input, ScriptVmArgs::ArgsOutput& output, const std::vector<const char*>* pModuls){//get the actual functionconst int nOrgSize = lua_gettop(m_pLua);int nInputSize = input.Size();int nOutputSize = output.Size();if (!pModuls || (pModuls && pModuls->empty())) //func is global{lua_getglobal(m_pLua, func);if (!lua_isfunction(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);goto failed;}}else{//trace down the modulesstd::vector<const char *>::const_iterator it = pModuls->begin();//get the global module name or the actual function name if there is no modulelua_getglobal(m_pLua, *it);if (!lua_istable(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);goto failed;}for (++it; it != pModuls->end(); ++it){lua_pushstring(m_pLua, *it);lua_gettable(m_pLua, -2);if (!lua_istable(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid table name: %s\n", *it);goto failed;}}//get the funclua_pushstring(m_pLua, func);lua_gettable(m_pLua, -2);if (!lua_isfunction(m_pLua, -1)){if (!bAllowNonexist)error_msg(true, "ExecuteScriptFunc: Invalid function name: %s\n", func);goto failed;}}/* push arguments */input.Push(*m_pLua);/* do the call */if (lua_pcall(m_pLua, nInputSize, nOutputSize, 0) != 0) /* do the call */{report_last_error(m_pLua, true);goto failed;}/* retrieve results */output.Pop(*m_pLua);failed://clear the stacklua_settop(m_pLua, nOrgSize);}int ScriptVM::GetConsumeMemoryCount(){int nCount = 0;if (m_pLua){nCount = lua_getgccount(m_pLua);}return nCount;}void ScriptVM::PrintGlobalVariables(){ExecuteScript("for n in pairs(_G) do print(n) end", true);}void ScriptVmArgs::DoPush(lua_State& lua, const char* pcArg){lua_pushstring(&lua, pcArg);}void ScriptVmArgs::DoPush(lua_State& lua, const bool bArg){lua_pushboolean(&lua, bArg);}void ScriptVmArgs::DoPush(lua_State& lua, const float fArg){lua_pushnumber(&lua, fArg);}void ScriptVmArgs::DoPush(lua_State& lua, const int nArg){lua_pushnumber(&lua, nArg);}void ScriptVmArgs::DoPush(lua_State& lua, unsigned uArg){lua_pushinteger(&lua, uArg);}void ScriptVmArgs::DoPop(lua_State& lua, bool& bArg, int idx){bArg = lua_toboolean(&lua, idx) != 0;}void ScriptVmArgs::DoPop(lua_State& lua, std::string& sArg, int idx){sArg = lua_tostring(&lua, idx);}void ScriptVmArgs::DoPop(lua_State& lua, float& fArg, int idx){fArg = (float)lua_tonumber(&lua, idx);}void ScriptVmArgs::DoPop(lua_State& lua, int& nArg, int idx){nArg = (int)lua_tonumber(&lua, idx);}void ScriptVmArgs::DoPop(lua_State& lua, unsigned& uArg, int idx){uArg = lua_tointeger(&lua, idx);}
OK,下面来实现一下测试代码,来看看如何 lua 和 c++进行交互的。

我们来简单编写几个文件。
函数调用:
ScriptFunction.h
/**************************************************************************************vic.MINg 2018/09/19***************************************************************************************/#pragma oncenamespace Script {//tolua_beginvoid show_luaproject_info();//tolua_end}
ScriptFunction.cpp
#include "ScriptFunction.h"#include <stdio.h>namespace Script {void show_luaproject_info(){printf("Show LuaProject C++ Info.\n");}}
结构体:
Hag.h
/***********************************************************************vic. MINg 2018/09/25***********************************************************************/#pragma once#include <string>//tolua_beginstruct Hag{int hag_id;std::string hag_name;bool is_valid;int hag_score;Hag() { }virtual ~Hag() {}};//tolua_end
类:
Student.h
#pragma once#include<iostream>using namespace std;//tolua_beginclass Student{public:Student();virtual ~Student();void Run();void Run(int a);};//tolua_end
Student.cpp
#include "Student.h"Student::Student(){}void Student::Run(){//cout << "Student Run" << endl;printf("Student Student Run.\n");}void Student::Run(int a){//cout << "Student Run" <<a<< endl;printf("Student Student Run with %d.\n", a);}Student::~Student(){}
非常简单的测试代码。不言语。继续往下。我们需要编写一份 LuaScript.pkg 文件来导出这些文件,让lua可以调用他们。
LuaScript.pkg
#include "tolua++.h"$#pragma warning(disable : 4800) //forcing value to bool warning// script functions$cfile "ScriptFunction.h"$using namespace Script;// class$cfile "Hag.h"$cfile "Student.h"
做一个实行脚本 glue.bat 来通过 LuaScript.pkg 编译 lua 的cpp文件。
glue.bat
..\..\Debug\tolua -o l_LuaScript.cpp LuaScript.pkgpause
这样会生成 l_LuaScript.cpp 来实行lua和c++ 之间的相互调用,这里由于是生成出来的 就不粘代码了
值得注意的几个方面:使用 boost 的时候为了方便 我们使用了 BOOST_ALL_NO_LIB ,这样 就不用编译 boost了
// "boost/config/user.hpp" 取消 BOOST_ALL_NO_LIB 注释#define BOOST_ALL_NO_LIB
注意 tolua中 注释的使用,如果没使用 //tolua_export、//tolua_begin、//tolua_end 是无法在实行 glue.bat 时生成的 l_LuaScript.cpp 中生成关联映射的。
工程配置:附加包含目录:..;..\include;....\lua\src;....\tolua\include; 附加依赖项:lua.lib;tolua.lib; 预处理器定义:WIN32;_CRT_SECURE_NO_WARNINGS;
main.cpp
#include "ScriptVM.h"#include "ScriptVmArgs.h"#include <stdio.h>#include <string>#include <assert.h>#include <iostream>extern "C" {#include "lua.h"#include "lualib.h"#include "lauxlib.h"}#include "tolua++.h"#include "IOServer.h"#include "Stream.h"#include "Hag.h"#include "FSWrapper.h"#include "boost/ref.hpp"extern void report_last_error(lua_State *L, bool bUseAssert);extern int tolua_LuaScript_open(lua_State* tolua_S);static int OverrideDofile(lua_State * L) {int n = lua_gettop(L); /* number of arguments */lua_getglobal(L, "tostring");if (n != 1)return luaL_error(L, "'dofile' param error!");std::string sFileName = "";lua_pushvalue(L, -1); /* function to be called */lua_pushvalue(L, 1); /* value to print */lua_call(L, 1, 1);sFileName = lua_tostring(L, -1); /* get result */if (sFileName == "")return luaL_error(L, "'dofile' param error!");sFileName = Internal::FSWrapper::GetBinDirectory().c_str();sFileName.append("/Scripts/test.lua");lua_pop(L, 1); /* pop result *///get chunk name as modified script namestd::string sChunkName(sFileName.c_str());for (unsigned int i = 0; i < sChunkName.length(); i++){if (sChunkName[i] == '/' || sChunkName[i] == '.')sChunkName[i] = '_';}//load it firstchar * pBuff = nullptr;int nSize;if (ScriptVM::LoadFileToBuffer(sFileName.c_str(), pBuff, nSize, true)){luaL_loadbuffer(L, (char *)pBuff, nSize, sFileName.c_str());delete[] pBuff;//luaL_loadfile(L, sScriptName);lua_setglobal(L, sChunkName.c_str());lua_getglobal(L, sChunkName.c_str());if (lua_pcall(L, 0, 0, 0) != 0){//printf("文件 " << sFileName.c_str() << " 执行错误!");std::string sErrorMsg = "error executing script file %s: ";sErrorMsg += sFileName;assert(sErrorMsg.c_str());//error_msg(true, "error executing script file %s: ", sFileName.AsCharPtr());report_last_error(L, true);}}else{lua_settop(L, n);return luaL_error(L, "error in the file!");}return 0;}int main(int argc, char** argv){ScriptVM* pScriptVM = new ScriptVM;if (!pScriptVM->Init()){printf("ScriptVM Init failed! \n");return -1;}pScriptVM->RegistGlobalFunction("dofile", OverrideDofile);// 注意调用 tolua_LuaScript_open ,使lua和c++可以相互调用tolua_LuaScript_open(pScriptVM->GetLuaState());std::string strScriptLua;#ifdef _WIN32strScriptLua = DirectoryManager::GetExeDirectory() + "scripts/scriptvm_hello.lua";#elsestrScriptLua = "scripts/scriptvm_hello.lua";#endif// 加载一个lua脚本pScriptVM->ExecuteScriptFile(strScriptLua.c_str());// 调用 lua 中的 student_run 方法 , ("i", 20) 表示传参, i 表示传一个参数 为int型// 假设要传 两个参数 一个是 double 一个是 std::string 可以写成 ("ds", 3.14, "vic.MINg")std::vector<const char *>modules;pScriptVM->ExecuteScriptFunc(modules, "student_run", false, "i", 20);// 调用 lua 中的 add 方法// inTuple 为进参、 outTuple 为出参// 调用方法如下int a = 20, b = 10;int sum = 0;boost::tuple<int, int> inTuple = boost::tuples::make_tuple(a, b);boost::tuple<int&> outTuple = boost::tuples::make_tuple(boost::ref(sum));pScriptVM->ExecuteFunc("add", false, inTuple, outTuple);printf("Lua Func Add %d + %d = %d \n", a, b, sum);// 这里我们 传一个字符串 来在lua中 new一个 新类// 看似比较平庸,我们可以通过某些文件来动态生成一个类// 比如 我们在 xml 中定义一个 <HagType>PlayerHag</HagType>// 然后 读取该项 然后 类型下面代码 生成 PlayerHag, 然后通过 RTTI 来判断 应用该类std::string strHag = "english_hag = nil english_hag = Hag:new()";pScriptVM->ExecuteScript(strHag.c_str());Hag* pEnglish_hag = static_cast<Hag*>(pScriptVM->GetGlobalUserdata("english_hag"));if (pEnglish_hag){pEnglish_hag->hag_id = 1;pEnglish_hag->hag_name = "English";pEnglish_hag->is_valid = true;pEnglish_hag->hag_score = 94;}// 通过 lua 快速公式计算std::string strFormula = "1.8586*level + 8.7842 +50+(level-1)*5/3";std::map<std::string, double> mapParams;mapParams.insert(std::make_pair( "level", 3));double dValue = pScriptVM->ComputingFormula(strFormula.c_str(), mapParams);printf("Lua ComputingFormula level = %d, 1.8586*level + 8.7842 +50+(level-1)*5/3 = %lf \n", 3, dValue);// 通过 lua 条件判断std::string strCondition = "(command_id == 1001) and (command_execution_stage == 0)";//std::string strCondition = "command_id == 1001";std::map<std::string, int> mapParamsCondition;mapParamsCondition.insert(std::make_pair("command_id", 1002));mapParamsCondition.insert(std::make_pair("command_execution_stage", 0));bool bValue = pScriptVM->ComparingCondition(strCondition.c_str(), mapParamsCondition);printf("Lua ComparingCondition ( command_id == 1001 AND command_execution_stage == 0 ) return %d \n", bValue);system("pause");delete pScriptVM;return 0;}
LuaScript 工程
这个项目 使用BabeLua插件创建,方便lua脚本的编写和调试。
scriptvm_hello.lua
str = "I am so cool"tbl = {name = "shun", id = 20114442}function add(a,b)return a + b + math.piendfunction student_run(n)local st = Student:new()st:Run(n)st:delete()show_luaproject_info()end
OK 这样一套完善的lua与c++相互交互体系就这样实现了,运行结果如下~





