Lua与C/C++交互——C/C++调用Lua脚本

Posted 牧秦丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua与C/C++交互——C/C++调用Lua脚本相关的知识,希望对你有一定的参考价值。

                                                                                   Lua与C/C++交互——C/C++调用Lua脚本

 

                                                                                                                                                                                                               点此下载代码资源

0.简介

        上期介绍了如何在Lua中调用C/C++代码,本期介绍如何在C/C++中调用Lua脚本。有关Lua与C/C++交互的基础知识以及Lua堆栈操作,请见《Lua与C/C++交互——Lua调用C/C++》一文。

 

        本期介绍一个例子,通过Lua来生成一个XML格式的便签。便签格式如下:

<?xml version="1.0" encoding="utf-8" ?>
<note>
	<fromName>发送方姓名</fromName>
	<toName>接收方姓名</toName>
	<sendTime>发送时间</sendTime>
	<msgContent>便签内容</msgContent>
</note>

        我们通过C/C++来输入这些信息,然后让Lua来生成这样一个便签文件。


1.  Lua代码

xmlHead = '<?xml version="1.0" encoding="utf-8" ?>\\n'

-- Open note file to wriet.
function openNoteFile(fileName)
	return io.open(fileName, "w")
end


-- Close writed note file.
function closeNoteFile(noteFile)
	noteFile:close()
end


function writeNestedLabel(ioChanel, label, nestCnt)
	if nestCnt == 0 then
		ioChanel:write(label)
		return
	end

	for i = 1, nestCnt do
		ioChanel:write("\\t")
	end

	ioChanel:write(label)
end


function generateNoteXML(fromName, toName, msgContent)
	local noteFile = openNoteFile(fromName .. "_" .. toName .. ".xml")
	if not noteFile then
		return false
	end

	noteFile:write(xmlHead)
	noteFile:write("<note>\\n")

	local currNestCnt = 1
	writeNestedLabel(noteFile, "<fromName>", currNestCnt)
	noteFile:write(fromName)
	writeNestedLabel(noteFile, "</fromName>\\n", 0)

	writeNestedLabel(noteFile, "<toName>", currNestCnt)
	noteFile:write(toName)
	writeNestedLabel(noteFile, "</toName>\\n", 0)

	local sendTime = os.time()
	writeNestedLabel(noteFile, "<sendTime>", currNestCnt)
	noteFile:write(sendTime)
	writeNestedLabel(noteFile, "</sendTime>\\n", 0)

	writeNestedLabel(noteFile, "<msgContent>", currNestCnt)
	noteFile:write(msgContent)
	writeNestedLabel(noteFile, "</msgContent>\\n", 0)

	noteFile:write("</note>\\n")
	closeNoteFile(noteFile)

	return true
end

         我们通过openNoteFile和closeNoteFile来打开/关闭XML文件。generateNoteXML全局函数接受发送方姓名、接收方姓名、便签内容,生成一个XML便签文件。便签发送时间通过Lua标准库os.time()函数来获取。writeNestedLabel函数根据当前XML的缩进数目来规范XML输出格式。此文件很好理解,不再赘述。


2.C++调用Lua脚本

extern "C"

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#pragma comment(lib, "lua.lib")
;


#include <iostream>
#include <string>
using namespace std;


//
// 初始化Lua环境.
//
lua_State* initLuaEnv()

	lua_State* luaEnv = lua_open();
	luaopen_base(luaEnv);
	luaL_openlibs(luaEnv);

	return luaEnv;



//
// 加载Lua文件到Lua运行时环境中.
//
bool loadLuaFile(lua_State* luaEnv, const string& fileName)

	int result = luaL_loadfile(luaEnv, fileName.c_str());
	if (result)
	
		return false;
	
	
	result = lua_pcall(luaEnv, 0, 0, 0);
	return result == 0;



//
// 获取全局函数.
//
lua_CFunction getGlobalProc(lua_State* luaEnv, const string& procName)

	lua_getglobal(luaEnv, procName.c_str());
	if (!lua_iscfunction(luaEnv, 1))
	
		return 0;
	

	return lua_tocfunction(luaEnv, 1);



int main()

	// 初始化Lua运行时环境.
	lua_State* luaEnv = initLuaEnv();
	if (!luaEnv)
	
		return -1;
	

	// 加载脚本到Lua环境中.
	if (!loadLuaFile(luaEnv, ".\\\\GenerateNoteXML.lua"))
	
		cout<<"Load Lua File FAILED!"<<endl;
		return -1;
	

	// 获取Note信息.
	string fromName;
	string toName;
	string msgContent;

	cout<<"Enter your name:"<<endl;
	cin>>fromName;

	cout<<"\\nEnter destination name:"<<endl;
	cin>>toName;

	cout<<"\\nEnter message content:"<<endl;
	getline(cin, msgContent);
	getline(cin, msgContent);

	// 将要调用的函数和函数调用参数入栈.
	lua_getglobal(luaEnv, "generateNoteXML");
	lua_pushstring(luaEnv, fromName.c_str());
	lua_pushstring(luaEnv, toName.c_str());
	lua_pushstring(luaEnv, msgContent.c_str());

	// 调用Lua函数(3个参数,一个返回值).
	lua_pcall(luaEnv, 3, 1, 0);

	// 获取返回值.
	if (lua_isboolean(luaEnv, -1))
	
		int success = lua_toboolean(luaEnv, -1);
		if (success)
		
			cout<<"\\nGenerate Note File Successful!"<<endl;
		
	

	// 将返回值出栈.
	lua_pop(luaEnv, 1);

	// 释放Lua运行时环境.
	lua_close(luaEnv);

	system("pause");
	return 0;


//:-)

3.代码解析

3.1.初始化Lua运行时环境

    lua_State*所指向的结构中封装了Lua的运行时环境。我们用initLuaEnv这个函数来初始化。lua_open函数用来获取一个新环境,然后我们用luaopen_base打开Lua的基础库,然后用luaL_openlibs打开Lua的io、string、math、table等高级库。


3.2.加载Lua文件

         然后我们用luaL_loadfile和lua_pcall来将一个Lua脚本加载到对应的Lua运行时环境中——注意:并不自动执行,只是加载。这两个函数如果返回非0,表示加载失败——你的Lua脚本文件可能未找到或Lua脚本有语法错误……


3.3.加载Lua函数

        我们用lua_getglobal函数将Lua脚本中的全局函数、全局变量等入栈,放在栈顶。


3.4.压入Lua函数调用参数

        我们用lua_push系列函数来将要调用函数所需参数全部入栈,入栈顺序为Lua函数对应参数从左到右的顺序。


3.5.调用Lua函数

        最后用lua_pcall来调用函数。Lua_pcall函数原型如下:

int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc);

       其中,L为此函数执行的Lua环境,nargs为此函数所需的参数个数,nresults为此函数的返回值个数,errfunc为发生错误时错误处理函数在堆栈中的索引。


3.6.获取Lua函数返回值

       然后,我们可以通过检测栈顶的位置(从-1开始),来获取返回值。获取返回值后,用lua_pop将栈顶元素出栈——出栈个数为返回值个数。


3.7.释放Lua环境

        最后,通过lua_close函数来关闭Lua环境并释放资源。


4.运行结果

        我们将GenerateNoteXML.lua脚本和最终的C++生成的GenerateNoteXML.exe放在同一路径下,并运行GenerateNoteXML.exe,在此目录下会生成一个XML文件。如下:




生成的arnozhang_YaFengZhang.xml文件如下:






以上是关于Lua与C/C++交互——C/C++调用Lua脚本的主要内容,如果未能解决你的问题,请参考以下文章

Lua封装&C++实践——Lua和C/C++的基本交互

Lua封装&C++实践——Lua和C/C++的基本交互

搭建Lua与C/C++交互的环境

搭建Lua与C/C++交互的环境

Unity中C#与Lua的交互

Lua与C/C++交互