先来说下大致脚本引擎框架,此次采用如下,即运行C++代码启动程序,然后加载Lua脚本执行!
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脚本之间的交互是双向的!
Lua和C/C++语言通信的主要方法是一个Lua的虚拟栈,栈的特点是先进后出。
在Lua中,Lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-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);
}
(1)新建一个Lua文件,test.lua 具体如下:
print("========Lua脚本=======")
--add为C导入Lua函数
sum = add(10,20)
print("计算结果为:")
print(sum)
print("========Lua脚本=======")
(2)建立一个控制台程序如下:
#include
using 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;
}
执行结果如下:
---先创建有个CLua类
.h文件
#ifndef __CLUA_H__
#define __CLUA_H__
struct lua_State;
#define LuaGlue extern "C" int
extern "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);
}
#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;
}
测试结果