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

前言

写过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的编写上,好了,这篇文章献上,希望对大家有帮助。如果你觉的还不错,可以将这篇文章分享给更多的朋友。当然了,你也可以扫描页面右侧的二维码资助我写出更好的文章了,那定是极好的。

时间: 2024-09-30 06:10:02

Lua和C++语言的交互详解_Lua的相关文章

Lua面向对象之多重继承、私密性详解_Lua

在Lua中的多重继承和私密性可能用得比较少,也可能只是我个人用得比较少. 本来想偷懒不写这文章的,因为我今天刚买了个漂移板,连起步都还没学会啊,想多学一会. 咳咳,本着坚持不懈.负责到底的态度,我还是决定随便写几句~(小若:随便写几句是几吨意思啊?!) 1.多重继承之在多个类中查找一个字段 我发现这些高(shen)智(jing)商(bing)人群真的很厉害,这种技巧都能想到,很佩服. 其实多重继承没什么特别的,除非两个将要被继承的类有相同的函数名和属性,否则,处理起来很简单.   无非就是在多个

Lua中的持久化和序列化详解_Lua

持久化 持久化(Persistence),即把内存中的对象保存到可永久保存的存储设备中.持久化的主要应用是将内存中的对象存储在关系型的数据库中,当然也可以存储在磁盘文件中.XML数据文件中等等. 持久化是将程序数据在持久状态和瞬时状态间转换的机制.(应用与游戏,) JDBC就是一种持久化机制.文件IO也是一种持久化机制. 为什么需要持久化服务呢?那是由于内存本身的缺陷引起的:内存掉电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号,遗憾的是,人们还无法保证内存永不掉电. 持久化方案

C/C++和Java的交互详解_java

安卓中支持c++(NDK)和java(SDK)语言,当使用到c++语言时,c++代码和java如何交互就尤为重要.在下载的NDK包中samples/hello-jni有一个简单的实例可以参考. java调用C++ 新建Android项目,创建如下类: package com.example.testjni; public class TextJni { // support to c static { System.loadLibrary("jniinterface"); } publ

《C语言开发从入门到精通》一第2章 C语言开发工具详解2.1 用DOS开发C程序

第2章 C语言开发工具详解 C语言开发从入门到精通 古人云:工欲善其事,必先利其器.由第1章的内容我们了解到,C语言开发工作需要使用专门的开发工具,这样才能起到事半功倍的效果.在本章的内容中,将简要介绍常用的几种C语言开发工具,详细介绍它们的安装和使用方法,为读者步入本书后面知识的学习打下基础. 本章内容 用DOS开发C程序 在Windows环境下开发C程序 在Linux下开发C程序 技术解惑 安装Visual Studio 2010的几个常见问题 有没有轻量级的.可以复制和粘贴代码的工具 Wi

AngularJS 指令的交互详解及实例代码_AngularJS

背景介绍 这例子是视频中的例子,有一个动感超人,有三种能力,力量strength,速度speed,发光light. 这三种能力作为三种属性,定义动感超人作为一个标签,只要添加对应的属性就能拥有该能力. 为了便于结果的展示,为标签添加鼠标的响应事件,当鼠标移动到对应的标签上就会触发一个方法,打印出具备的能力. 程序分析 html部分的代码如下:        <div> <superman>nothing!</superman> <superman strength

C语言 指针数组详解及示例代码_C 语言

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组.指针数组的定义形式一般为: dataType *arrayName[length]; [ ]的优先级高于*,该定义形式应该理解为: dataType *(arrayName[length]); 括号里面说明arrayName是一个数组,包含了length个元素,括号外面说明每个元素的类型为dataType *. 除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子: #include <stdi

C语言 字符串指针详解及示例代码_C 语言

C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中,这在<C语言字符数组和字符串>中已经进行了详细讲解,这里不妨再来演示一下: #include <stdio.h> int main(){ char str[] = "http://c.biancheng.net"; int len = strlen(str), i; //直接输出字符串 printf("%s\n", str); //每次输出一个字符 for(i=0; i<

C语言 二级指针详解及示例代码_C 语言

指针可以指向一份普通类型的数据,例如 int.double.char 等,也可以指向一份指针类型的数据,例如 int *.double *.char * 等. 如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针. 假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示: 将这种关系转换为C语言代码: int a =100; int *p1 = &a; int **p2 = &p1; 指针变量也是一种变量

C语言 位运算详解及示例代码_C 语言

所谓位运算,就是对一个比特(Bit)位进行操作.在<二进制思想以及数据的存储>一节中讲到,比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了. C语言提供了六种位运算符: 运算符 & | ^ ~ << >> 说明 按位与 按位或 按位异或 取反 左移 右移 按位与运算(&) 一个比特(Bit)位只有 0 和 1 两个取值,只有参与&运算的两个位都为 1 时,结果才为 1,否则为 0.例如1&1