Lua和C++语言的交互详解

前言

写过Windows程序的人都知道,对于应用程序,如果需要在本地保存一些配置信息,我们经常将这些配置信息写在注册表或者本地的配置文件中,很多应用都是将一些配置信息写在配置文件中,比如以ini结尾的文件,这种配置文件很多,使用的很广泛,然后应用程序在启动的时候,就会解析这个配置文件,读取一些配置信息。

Lua的一项重要用途就是作为一种配置语言。而这篇文章将结合Lua来扩展应用程序,这种方式提供了更大的灵活性和便利性。

这篇博文主要总结的是使用C++和Lua进行交互,涉及到获取Lua中普通变量的值,Lua中table的值和调用Lua中的函数。下面就开始吧。

从一个最简单的例子开始

一个GUI程序,从配置文件读取窗口的大小,从而实现设置窗口的大小。下面我就写一个基于MFC的窗体程序来完成这个功能。点击这里去下载完成代码工程。我把重点的代码贴出来:

代码如下:

bool CLuaConfig::LoadConfig()
{
    L = luaL_newstate();
    if (!L)
    {
        return false;
    }
 
    // 加载配置文件
    int bRet = luaL_loadfile(L, pConfigFile);
    if (bRet)
    {
        return false;
    }
    // 运行配置文件
    bRet = lua_pcall(L, 0, 0, 0);
    if (bRet)
    {
        return false;
    }
 
    // 读取高
    lua_getglobal(L, "width");
    lua_getglobal(L, "height");
 
    // width
    if (!lua_isnumber(L, -2))
    {
        return false;
    }
 
    // height
    if (!lua_isnumber(L, -1))
    {
        return false;
    }
    iWindowHeight = lua_tointeger(L, -1);
    iWindowWidth = lua_tointeger(L, -2);
    return true;
}

luaL_newstate就不说了,用烂了;luaL_loadfile用于加载一个lua文件,然后调用lua_pcall运行编译好的程序块,lua_pcall是在保护模式下运行Lua代码,也就是说,出错了,lua_pcall会返回一个错误代码,并不会直接crash。当运行完程序块后,调用了两次lua_getglobal函数,这个函数会将全局变量值压入栈中,所以,width的值在索引为-2的位置,height的值在索引为-1的位置;接下来,就不用多说了。就是这样。下载程序,运行一下,就OK了,修改代码文件夹下的config.lua文件,看看运行结果。源代码这里下载。

table操作

在Lua中,对于table这种bug一样存在的东西,如果C API无法操作table,那我们还能不能愉快的玩耍了。让我们来看看C API如何操作table。现在有如下Lua语句:

代码如下:

background = {r = 0.3, g = 1, b = 0.5}

那么,C API如何读取这段代码,将其中的每个字段都解析出来呢。我先把代码贴上来,然后一句一句的分析:

代码如下:

// 读取全局的数据到栈中
lua_getglobal(L, "background");
if (!lua_istable(L, -1))
{
    // 如果不是table,就显示错误信息
    cout << "It's not a table." << endl;
    return 0;
}
 
// 读取table中字段的值,将值压入栈中
lua_getfield(L, -1, "r");
 
// 读取栈中的值
if (!lua_isnumber(L, -1))
{
    // 如果不是实数,就显示错误信息
    cout << "It's not a number." << endl;
    return 0;
}
 
double fValue = lua_tonumber(L, -1);
cout << "r => " << fValue << endl;

原谅我省略了luaL_newstate这样的代码。好了,读取一个table,同读取一个全局的变量是一个道理的。分为以下几步:

1.使用lua_getglobal读取这个变量,将table读取到栈中;
2.使用lua_getfield读取table中字段的值,将字段的值读取到栈中;
3.使用lua_to*系列函数,将字段的值从栈中读取出来。

这是读取table的操作,那设置table的操作呢?我们可以将我们自己的值写入到栈中,这该怎么操作?看代码:

代码如下:

// 将需要设置的值设置到栈中
lua_pushnumber(L, 0.55);
 
// 将这个值设置到table中
lua_setfield(L, -2, "r");

就是上面两行代码,当然了,你也需要先使用lua_getglobal读取table变量,将table读取到栈中,然后按照上面的两行代码进行设置就OK了。上面两行代码的具体含义是什么呢?

1.lua_push*系列函数是将一个需要设置的新值放到栈中;

2.lua_setfield函数同lua_getfield是一个性质的函数,只不过这里是set语义,将lua_push*到栈中的值,设置到table对应的key中。

现在读取table,设置table都说了,那如何在表中完全创建一个新的table呢?我们有这种需求。怎么办?

代码如下:

// 创建一个新的table,并压入栈
lua_newtable(L);
 
// 往table中设置值
lua_pushstring(L, "http://www.jb51.net"); // 先将值压入栈
lua_setfield(L, -2, "website"); // 将值设置到table中
 
// 再设置一个值
lua_pushstring(L, "果冻想 | 一个原创文章分享网站");
lua_setfield(L, -2, "description");

我将重要的几行代码贴上来了,最重要的就是一个lua_newtable函数,该函数会创建一个新的table,并将这个table置于栈中,接下来就和上面设置table的值是一样的。源代码下载一、下载二。

调用Lua函数

是的,你没有看错,你可以在一lua文件中定义一个函数,然后在C++中调用这个函数,貌似“高大上”的感觉。现在我就来说说这个“高大上”的功能;习惯性的上代码:

代码如下:

// 再来看看有参数和返回值得函数调用
// 现在在test.lua中定义了一个add函数,计算两个值的和,这两个值就是用参数传进去的
// 得到和以后,会返回这个和,现在我们就在C++这边调用这个add函数
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
 
// 完成调用
iRet = lua_pcall(L, 2, 1, 0);
if (iRet)
{
    const char *pErrorMsg = lua_tostring(L, -1);
    cout << pErrorMsg << endl;
    lua_close(L);
    return 0;
}
 
// 获得计算结果
iRet = lua_isnumber(L, -1);
if (!iRet)
{
    cout << "Error occured." << endl;
    lua_close(L);
    return 0;
}
 
double fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;

上面代码是调用以下lua函数:

代码如下:

-- 有参数,有返回值
function add(iA, iB)
    return iA + iB
end

这个简单的Lua函数没有任何讲的地方,说说上面的那一长段C++代码吧。在Lua中,函数和普通的值是一样的,所以,C++调用Lua中的函数,分为以下几步:

使用lua_getglobal来获取函数,然后将其压入栈;

如果这个函数有参数的话,就需要依次将函数的参数也压入栈;

这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;

最后取返回值得过程不用多说了,调用完毕。

源代码这里下载。

总结

到此这篇文章总结完毕,总共花费4天的业余的零碎时间,时间主要花费在demo的编写上,好了,这篇文章献上,希望对大家有帮助。如果你觉的还不错,可以将这篇文章分享给更多的朋友。当然了,你也可以扫描页面右侧的二维码资助我写出更好的文章了,那定是极好的。

(0)

相关推荐

  • 简单谈谈lua和c的交互

    介绍 lua和c的亲密接触,靠的是一个虚拟栈.lua通过这个虚拟栈来实现和c之间值的互传.栈上的每一个元素是一个lua值(nil,number,string...). 当lua调用c函数的时候,这个函数会得到一个新的栈,这个栈独立于c函数本身的栈,也独立于lua自己的栈.它里面包含了lua要传给c的所有参数,然后c函数会把返回的结果放入这个栈中返回给调用者. 对于栈的查询操作,如果按照栈的规则,只能拿到栈顶的元素.但这里和常规的栈有一些差异.就是可以用一个索引来指向栈上的任何元素.正数的索引(1

  • Lua和C语言的交互详解

    前言 对于Lua的基础总结总算告一段落了,从这篇博文开始,我们才真正的进入Lua的世界,一个无聊而又有趣的世界.来吧. Lua语言是一种嵌入式语言,它本身的威力有限:当Lua遇见了C,那它就展示了它的强大威力.C和Lua是可以相互调用的.第一种情况是,C语言拥有控制权,Lua是一个库,这种形式中的C代码称为"应用程序代码":第二种情况是,Lua拥有控制权,C语言是一个库,这个时候C代码就是"库代码"."应用程序代码"和"库代码"

  • Lua与C语言间的交互实例

    Lua 是一门轻巧.灵活.扩展性很强的脚本语言,它可以很容易的嵌入到其他语言(C/C++)中使用,这主要得益于其提供了功能强大的 C API,这让其跟 C/C++ 间的互调成为一件很轻松的事. Lua 调用 C Lua 调用 C 函数,其实就是把 C 函数注册到 Lua 中去,把 C 函数地址传递给 Lua 解释器.这个传递是要遵循一个的协议的,即: 复制代码 代码如下: typedef int (*lua_CFunction)(lua_State* L) Lua 和 C 是通过栈(State)

  • Lua和C++语言的交互详解

    前言 写过Windows程序的人都知道,对于应用程序,如果需要在本地保存一些配置信息,我们经常将这些配置信息写在注册表或者本地的配置文件中,很多应用都是将一些配置信息写在配置文件中,比如以ini结尾的文件,这种配置文件很多,使用的很广泛,然后应用程序在启动的时候,就会解析这个配置文件,读取一些配置信息. Lua的一项重要用途就是作为一种配置语言.而这篇文章将结合Lua来扩展应用程序,这种方式提供了更大的灵活性和便利性. 这篇博文主要总结的是使用C++和Lua进行交互,涉及到获取Lua中普通变量的

  • python与xml数据的交互详解

    目录 一 什么是XML? 二 XML语法规则 1. xml语法规则 2. xml与html的区别 三 python与xml的交互 1. 获取标签对内的数据 2. 获取标签属性值 一 什么是XML? python与json数据的交互详情 在这篇文章中我们介绍了json是一种独立于编程语言和平台的数据存储和交换方式(格式),其实xml和json基本一样,也是一种用于进行数据存储和交换的方式,并且也独立于编程语言和平台.XML可扩展标记语言(英语:Extensible Markup Language,

  • IOS ObjectC与javascript交互详解及实现代码

    IOS OC与js交互详解 JS注入 : 把JS代码有OC注入到网页 JS注入又叫做OC和JS的交互 OC和JS的交互需要一个桥梁(中介),这个桥梁就是UIWebView的代理方法 网页加载初始内容 #import "ViewController.h" @interface ViewController ()<UIWebViewDelegate> @property (weak, nonatomic) IBOutlet UIWebView *webView; @end -

  • 易语言子程序知识点详解

    将程序分割成较小的逻辑组件就可以简化程序设计任务,这些逻辑组件被称为子程序. 子程序可用于压缩重复任务或共享任务,例如,压缩频繁的计算处理等等. 用子程序编程有两大好处: 子程序可使程序划分成离散的逻辑组件,每个组件都比无子程序的整个程序容易调试及理解: 一个应用程序中的子程序,往往不必修改或只需稍作改动,便可以成为另一个程序的子程序. 每次调用子程序时,子程序中的所有语句都将被从第一条开始顺序执行,当执行到子程序尾部或者遇到"返回"命令时即返回到调用此子程序语句的下一条语句处. 子程

  • Spring表达式语言SpEL用法详解

    这篇文章主要介绍了spring表达式语言SpEL用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 (1)spring表达式语言是一个支持运行时查询和操作对象图得我强大表达式语言. (2)语言类似于EL:SpEL使用#{...}作为定界符.所有在大括号中的字符串均被认为是SpEL. (3)SpEL为bean的属性进行动态赋值提供了便利. (4)通过SpEL可以实现: 通过Bean的id对Bean进行引用 调用方法及引用对象的属性 计算表达式

  • R语言数据类型深入详解

    R语言用来存储数据的对象包括: 向量, 因子, 数组, 矩阵, 数据框, 时间序列(ts)以及列表 意义介绍 1. 向量(一维数据): 只能存放同一类型的数据 语法: c(data1, data2, ...),访问的时候下标从1开始(和Matlab相同);向量里面只能存放相同类型的数据. > x <- c(1,5,8,9,1,2,5) > x [1] 1 5 8 9 1 2 5 > y <- c(1,"zhao") # 这里面有integer和字符串, 整

  • R语言关联规则深入详解

    在用R语言做关联规则分析之前,我们先了解下关联规则的相关定义和解释. 关联规则的用途是从数据背后发现事物之间可能存在的关联或者联系,是无监督的机器学习方法,用于知识发现,而非预测. 关联规则挖掘过程主要包含两个阶段:第一阶段从资料集合中找出所有的高频项目组,第二阶段再由这些高频项目组中产生关联规则. 接下来,我们了解下关联规则的两个主要参数:支持度和置信度. 用简化的方式来理解这两个指标,支持度是两个关联物品同时出现的概率,而置信度是当一物品出现,则另一个物品也出现的概率. 假如有一条规则:牛肉

  • R语言“循环”知识点详解

    可能有一种情况,当你需要执行一段代码几次. 通常,顺序执行语句. 首先执行函数中的第一个语句,然后执行第二个语句,依此类推. 编程语言提供允许更复杂的执行路径的各种控制结构. 循环语句允许我们多次执行一个语句或一组语句,以下是大多数编程语言中循环语句的一般形式 - R编程语言提供以下种类的循环来处理循环需求. 单击以下链接以检查其详细信息. Sr.No. 循环类型和描述 1 repeat循环 多次执行一系列语句,并简化管理循环变量的代码. 2 while循环 在给定条件为真时,重复语句或语句组.

  • C语言字符串数组详解

    C语言字符串数组 字符串是连续的字符序列,最后以空字符'\0'作为终止符.一个字符串的长度指所有字符的数量,但不包括终止符.在 C 语言中,没有字符串类型,自然也就没有运算符以字符串为操作数. 字符串被存储在元素类型为 char 或宽字符类型数组中(宽字符类型指 wchar_t.char16_t 或 char32_t).宽字符组成的字符串也称为宽字符串(wide string). C 标准库提供了大量的函数,它们可以对字符串进行基本操作,例如字符串的比较.复制和连接等.在这些传统的字符串函数以外

随机推荐