Lua C/C++互相调用

      先来说下大致脚本引擎框架,此次采用如下,即运行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)建立一个控制台程序如下:

#include <iostream>
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;
}

执行结果如下:

5.简单封装2

---先创建有个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 <stdio.h>
#include <string.h>
#include <string>


#include <CLua.h>

extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

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 <stdio.h>
#include <string.h>
#include <CLua.h>

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

测试结果

代码下载 

posted @ 2019-03-06 00:29  jadeshu  阅读(366)  评论(0编辑  收藏  举报