【前言】

写过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, , , );
if (bRet)
{
return false;
} // 读取高
lua_getglobal(L, "width");
lua_getglobal(L, "height"); // width
if (!lua_isnumber(L, -))
{
return false;
} // height
if (!lua_isnumber(L, -))
{
return false;
}
iWindowHeight = lua_tointeger(L, -);
iWindowWidth = lua_tointeger(L, -);
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 = , b = 0.5}

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

// 读取全局的数据到栈中
lua_getglobal(L, "background");
if (!lua_istable(L, -))
{
// 如果不是table,就显示错误信息
cout << "It's not a table." << endl;
return ;
} // 读取table中字段的值,将值压入栈中
lua_getfield(L, -, "r"); // 读取栈中的值
if (!lua_isnumber(L, -))
{
// 如果不是实数,就显示错误信息
cout << "It's not a number." << endl;
return ;
} double fValue = lua_tonumber(L, -);
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, -, "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.jellythink.com"); // 先将值压入栈
lua_setfield(L, -, "website"); // 将值设置到table中 // 再设置一个值
lua_pushstring(L, "果冻想 | 一个原创文章分享网站");
lua_setfield(L, -, "description");

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

【调用Lua函数】

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

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

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

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

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

  1. 使用lua_getglobal来获取函数,然后将其压入栈;
  2. 如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
  3. 这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;
  4. 最后取返回值得过程不用多说了,调用完毕。

源代码这里下载。

C“控制”Lua的更多相关文章

  1. Lua“控制”C

    [前言] Lua语言本身是一个功能非常有限,而比较单调的语言,而且标准库也非常的平庸,它的NB之处就在于,它能和C.C++等高级语言完美“私通”.我们可以使用C.C++语言去给Lua写一个完美的库,让 ...

  2. Lua 与 Redis

    Lua 与 Redis 标签: Java与NoSQL 从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis - 案例-实现访问频率限制: 实现访问者 $ip 在一定的 ...

  3. lua元表Metatable

    Lua 中的每个值都可以用一个 metatable. 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为. 你可以通过在 metatable 中的特定 ...

  4. Lua 5.1 参考手册

    Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes 云风 译 www.codingno ...

  5. 关于Lua程序设计{读书笔记}

    1.lua中的标识符可以是由任意字母.数字和下划线构成的字符串,但不能以数字开头.2.lua将通常类似"_VALUE"的标识符作为保留标识符3.lua的保留字 and break ...

  6. 学习Lua setmetatable Lua 元表

    Lua 元表(Metatable) 在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作. 个人理解,这个相当于其他语言的继承,是把这个类的方法 ...

  7. Lua语言模型 与 Redis应用

    Lua语言模型 与 Redis应用 标签: Java与NoSQL 从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis. 本篇博客主要介绍了 Lua 语言不一样的设计 ...

  8. Lua教程

    Lua中的类型与值 Lua中的表达式 Lua中的语句 Lua中的函数 Lua中的闭包 Lua 中 pairs 和 ipairs 的区别 Lua中的迭代器与泛型for Lua中的协同程序 Lua中__i ...

  9. Redis结合Lua脚本实现高并发原子性操作

    从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis … 案例-实现访问频率限制: 实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次. 非脚 ...

随机推荐

  1. H5 俄罗斯方块Demo

    链接:http://pan.baidu.com/s/1hrDM0T2

  2. Mac之lnmp环境搭建

    之前在Windows上开发大部分都是使用的集成环境(xampp,phpstudy,wamp),可以完成日常便捷开发,有些时候却Windows下无法实现的就需要自己搭建虚拟机,在虚拟机中搭建lnmp环境 ...

  3. mysql 5.7 json

    项目中使用的mysql5.6数据库,数据库表一张表中存的字段为blob类型的json串数据.性能压测中涉及该json串处理效率比较低,开发人员提到mysql5.7版本后json串提供了原生态的json ...

  4. 基于MySQL的Activiti6引擎创建

    整个activiti6的搭建都是在spring boot2之上的,首先贴一下pom: <dependencies> <!-- 这是activiti需要的最基本的核心引擎 --> ...

  5. python中的编码问题

    遇到的问题: (1)ValueError: source code string cannot contain null bytes 发现文件的编码被改成了UTF-16BE,使用pycharm设置项目 ...

  6. Javascript初识之数据类型

    一.JavaScript概述 1.ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组 ...

  7. 关于vue-cli的项目结构【转】

    一.总体框架 一个vue-cli的项目结构如下,其中src文件夹是需要掌握的,所以本文也重点讲解其中的文件,至于其他相关文件,了解一下即可. vue-cli项目总体结构 二.文件结构细分 1.buil ...

  8. 使用css实现无滚动条滚动+使用插件自定义滚动条样式

    使用css实现无滚动条滚动,摘抄自:曹小萌博客 使用css实现无滚动条滚动,大体思路是在div外面再套一个div.这个div设置overflow:hidden.而内容div设置 overflow-x: ...

  9. tmux 使用说明

    安装Mac:brew install tmux若未安装libevent,需要先brew install libeventCentos:yum -y install tmuxUbuntu:apt-get ...

  10. 【BZOJ5491】[HNOI2019]多边形(模拟,组合计数)

    [HNOI2019]多边形(模拟,组合计数) 题面 洛谷 题解 突然特别想骂人,本来我考场现切了的,结果WA了几个点,刚刚拿代码一看有个地方忘记取模了. 首先发现终止态一定是所有点都向\(n\)连边( ...