|Lua 简介

Lua 是一种轻量小巧的脚本语言,也是号称性能最高的脚本语言,它用C语言编写并以源代码形式开放。

某些程序常常需要修改内容,而修改的内容不仅仅是数据,更要修改很多函数的行为。

而修改函数行为这种事,很难用简单的更改数据的方式来实现,若在源代码层面上改又得重新编译生成,导致修改成本高。

而脚本语言先通过更改数据,并加了一层对数据解释成运行代码的步骤,从而使程序能在运行时更改复杂的函数行为而无需重新编译。

它为程序大大地提供了灵活的扩展和定制功能,减少了修改的成本。

而游戏程序往往会选择性能高的LUA作为脚本,来应对某些经常修改的模块。

|编译、配置 Lua动态链接库

(本文使用Lua-5.3.5版本)

此外不建议编译配置Lua静态链接库,不然用到某些函数缺少dll会导致运行时错误

Lua库C源码:https://www.lua.org/download.html

下载lua-5.3.x.tar.gz文件,解压。

创建DLL项目,选择Release模式

将解压后的src文件夹下所有.h和.c文件(lua.c,luac.c和其他格式文件都不要)拖进项目,

预处理器定义(宏定义)加上LUA_BULD_AS_DLL

然后项目生成dll文件和lib文件,这两个文件就是编译好出来的动态链接库。

最后,在自己的工程项目里,

dll文件复制过来放在生成文件夹(第一次编译项目会在项目根目录生成的Debug/Release文件夹)里,

lib文件复制过来放在项目里某个目录,

那堆.h文件.c文件(lua.c,luac.c和其他格式文件都不要)也要复制过来放在项目里某个目录,

配置好项目的包含目录(放.h.c文件的那里)和库目录(放lib文件的那里)

至此,项目配置Lua库完成

然后可以在工程项目里如下代码包含lua库:

  1. #pragma comment(lib, "lua.lib")
  2. extern "C"
  3. {
  4. #include <lua.h>
  5. #include <lualib.h>
  6. #include <lauxlib.h>
  7. };

|Lua 基本语法(部分)

完整的语法教程->Lua编程参考文档:http://book.luaer.cn/

部分Lua的基本变量类型:

nil 无效值
boolean 只有两个值:false和true
number 双精度类型的实浮点数(Lua的数字类型只有双精度浮点数,并无整形单精度之分)
string 字符串由一对双引号或单引号来表示
function 由C或Lua编写的函数
table

Lua 中的表(table)其实是一个"关联数组",数组的索引可以是数字或者是字符串。

Lua在定义一个变量时,无需声明它的类型:

a = 12

b = 250.520

c = "hello world"

d = {name = "asd",id = 2333}

条件:

if xxx then

  xxxx

else

  xxxx

end

Lua的函数可以返还多个返还值

函数格式:

function xxx(xxxx)

end

|C与Lua的交互机制

在用C/C++使用Lua库前,有必要理解它们的交互机制。

C与Lua交互的基础是虚拟栈:

(如图所示)

此外,为了方便找到栈底栈顶元素的位置,这个虚拟栈还提供两种索引:

正数索引和负数索引,从而使-1总是代表栈顶元素的索引,1总是代表栈底元素的索引

交互基本原理:

当C要调用Lua数据时,Lua把值压入栈中,C再从栈中取值;

当Lua调用C数据时,C要将数据压入栈中,让Lua从栈中取值。

交互值时大部分可以按上面的互相传输,但是交互函数稍微更复杂:

当C要调用Lua函数时,Lua先将Lua函数压入栈中,C再将数据(作为参数)继续压入栈中,

然后用API调用栈上的lua函数+参数,调用完后,Lua函数和参数都会出栈,而函数计算后的返还值会压入栈中。

当Lua要调用C函数时,需要通过API注册符合lua规范的C函数,来让Lua知道该C函数的定义。

|C/C++调用Lua脚本

先编写一个测试用的Lua脚本文件,

(由于博主新装电脑,暂时直接用记事本编辑,但是没语法检查容易出错,这里推荐使用其它专业的lua编辑器,例如vsc,lua studio等)

打开lua脚本文件:

  1. char lua_filename[] = "test.lua";
  2. lua_State *L = load_lua(lua_filename);
  3. if (NULL == L) {
  4.   return -;
  5. }

读取lua文件的一般变量:

  1. lua_getglobal(L, "str");
  2. printf("str:%s\n",lua_tostring(L, -));
  3. lua_getglobal(L, "number");
  4. printf("number:%f\n", lua_tonumber(L, -));

读取lua文件的table里的变量:

  1. lua_getglobal(L, "table");
  2. //记录table的索引
  3. int tableIndex = lua_gettop(L);
  4.  
  5. //对-1位置的table取name变量压入栈顶
  6. lua_getfield(L, -, "name");
  7. printf("table:name:%s\n",lua_tostring(L, -));
  8.  
  9. //对tableIndex位置的table取table2变量压入栈顶
  10. lua_getfield(L, tableIndex, "table2");
  11. //对-1位置的table2取name2变量压入栈顶
  12. lua_getfield(L, -, "name2");
  13. printf("table:table2:name2:%s\n", lua_tostring(L, -));

读取lua文件的函数,并调用之:

  1. lua_getglobal(L, "add");//读取函数到栈顶
  2. lua_pushnumber(L, ); //压入参数 10
  3. lua_pushnumber(L, ); //压入参数 20
  4. //调用函数,若失败返还非0
  5. //lua_pcall第二个参数是指参数的数量,第三个参数是指返还值的数量
  6. if (lua_pcall(L, , , ) != ) {
  7. printf("lua_pcall failed: %s\n", lua_tostring(L, -));
  8. return -;
  9. }
  10. //读取目前栈顶的元素,也就是返还值
  11. double result = lua_tonumber(L, -);
  12. printf("add result:%f\n",result);

 

执行上述代码,我们便能看到如下结果

调用lua API简单总结:

将Lua脚本里的变量压入栈中

  1. //根据name获取某个全局变量,压入栈顶
  2. int lua_getglobal(lua_State *L, const char *name);
  3. //根据name获取index索引的table元素里的某个变量,压入栈顶
  4. int lua_getfield(lua_State *L, int index, const char *name);

将C变量压入栈中

  1. //将数字压入栈顶
  2. void lua_pushnumber(lua_State *L,double number);
  3. //将字符串压入栈顶
  4. const char *lua_pushstring(lua_State *L, const char *str);

将栈中某个位置的元素提取成C变量

  1. //将index索引的元素以数字的形式提取
  2. double lua_tonumber(lua_State *L, int index);
  3. //将index索引的元素以字符串的形式提取,返还
  4. const char* lua_tostring(lua_State *L, int index);

利用栈调用lua函数

  1. //调用lua函数,arguNum是参数的个数,returnNum是返还值的个数,errorHandleIndex是函数调用错误时会另外调用的错误处理函数的索引(0视为无)
  2. //调用前要求:依次压入 lua函数元素,第1个参数元素,第2个参数元素....
  3. //调用后:调用的lua函数元素和所有参数元素 会在栈里被清理掉,并且若干个返还值元素将压入栈顶
  4. int lua_pcall(lua_State *L, int arguNum, int returnNum, int errorHandleIndex);

|Lua脚本调用C函数

首先编写好要调用的C函数,

但是这个C函数并不会像我们往常编写的“正宗C函数”。

首先该函数格式应为:

  1. static int xxxxx(lua_State *L) {
  2.   //balabala随便做点事什么
  3. return 一个数字;
  4. }

xxxxx的返还值 代表 注册后该函数返还值的个数

那如何接受参数呢?这得通过上面介绍过的“将栈中某个位置的元素提取成C变量”方法获取参数。

那如何返还返还值呢?同样通过"将C变量压入栈中"方法将返还值压入栈顶。

例如1个参数、无返还值的print_num函数

  1. static int print_num(lua_State *L) {
  2. double a = lua_tonumber(L, -);
  3. printf("This num is %f", a);
    return ;
  4. }

有3个参数、1个返还值的add_three函数

  1. static int add_three(lua_State *L) {
  2. int a = lua_tonumber(L, -);
    int b = lua_tonumber(L, -);
    int c = lua_tonumber(L, -);
    int sum = a + b + c;
  3. lua_pushnumber(L, sum);
  4. return ;
  5. }

然后我们在代码里用API将上述C函数注册到Lua环境里:

第二个参数为在Lua脚本里要注册的函数名字,第三个参数为要注册的C函数指针

  1. lua_register(L, "print1", print_num);
  2. lua_register(L, "add3", add_three);

接下来修改脚本文件内容:

我们看看在C调用Lua脚本的callCFunc(),这个函数里面能不能正确调用回2个注册的C函数。

  1. char lua_filename[] = "test.lua";
  2. lua_State *L = load_lua(lua_filename);
  3. if (NULL == L) {
  4. return -;
  5. }
  6. // 注册函数
  7. lua_register(L, "print1", print_num);
  8. lua_register(L, "add3", add_three);
  9.  
  10. //调用Lua脚本的callCFunc函数
  11. lua_getglobal(L, "callCFunc");
  12. lua_pcall(L, , , );

结果如我们所料:

通过上面C与Lua的交互,我们发现它们的交互机制并不简单,

而且尚不支持C++这种更复杂的更多特性与Lua交互(类/对象/等)。

实际工程中我们往往不想将注意力放在交互的底层过程,而是想如何方便的直接使用交互。

于是可以使用github现有的库以已达到C++与Lua方便交互的作用。

|使用Kaguya C++ binding库

kaguya c++ binding下载地址:https://github.com/satoren/kaguya

kaguya是一个很易用的库,它github的使用说明也十分浅显易懂,就连它的配置也是十分简单的:

首先确保你的项目已经包含了lua5.1~lua5.3的环境,

然后只需在你的项目添加"kaguya/include"目录到项目的"头文件包含目录"即可。

本文就只简单示范它的几个用法(因为github的说明足够详细了,可自行查阅):

testkaguya.lua文件:

  1. str = "Im dont know what to write"
  2. number = 250.520
  3.  
  4. table = {
  5. name = "Ezio",
  6. id = ,
  7. table2 = {name2 = "Auditore",id2 = }
  8. }
  9.  
  10. function useCppClass(obj)
  11. obj:a()
  12. end

C++测试用代码:

  1. #include <iostream>
  2. #include <string>
  3. #include "kaguya/kaguya.hpp"
  4.  
  5. using namespace std;
  6.  
  7. class Base {
  8. private:
  9. int shit;
  10. public:
  11. virtual void a() {cout << "Base::a()";}
  12. };
  13.  
  14. class Derived : public Base {
  15. public:
  16. virtual void a() { cout << "Derived::b()"; }
  17. };
  18.  
  19. int main()
  20. {
  21. //----初始化-----//
  22. kaguya::State state;
  23. state.dofile("testkaguya.lua");
  24.  
  25. //-----执行lua代码-----//
  26. state("number2 = 233");
  27. state("str2 = 'ok'");
  28.  
  29. //-----值交互----//
  30. std::string value0 = state["str"];
  31. cout << value0 << endl << endl;
  32.  
  33. std::string value1 = state["str2"];
  34. cout << value1 << endl << endl;
  35.  
  36. double value2 = state["number"];
  37. cout << value2 << endl << endl;
  38.  
  39. std::string value3 = state["table"]["name"];
  40. cout << value3 << endl << endl;
  41.  
  42. state["tbl"] = kaguya::NewTable();
  43. state["tbl"]["value"] = ;
  44. double value4 = state["tbl"]["value"];
  45. cout << value4 << endl << endl;
  46.  
  47. //-----函数交互-----//
  48. int funcReturn1 = state["math"]["abs"](-);
  49. assert(funcReturn1 == );
  50.  
  51. auto funcReturn2 = state["math"]["abs"].call<int>(-);
  52. assert(funcReturn2 == );
  53. //-----类部分-----//
  54. state["Base"].setClass(kaguya::UserdataMetatable<Base>()
  55. .addFunction("a", &Base::a)
  56. );
  57. state["Derived"].setClass(kaguya::UserdataMetatable<Derived, Base>()
  58. .addFunction("a", &Derived::a)
  59. );
  60.  
  61. Base obj1;
  62. Derived obj2;
  63. state["useCppClass"](obj1);
  64. state["useCppClass"](obj2);
  65.  
  66. system("pause");
  67. return ;
  68. }

测试结果:

C++与Lua交互之配置&交互原理&示例的更多相关文章

  1. 通过lua栈了解lua与c的交互

    lua是如何执行的 其中分析.执行部分都是c语言实现的. lua与c的关系 lua的虚拟机是用c语言实现的,换句话说一段lua指令最终在执行时都是当作c语言来执行的,lua的global表,函数调用栈 ...

  2. 小睿开始呼叫用户,然后FS怎么跟用户交互的整个流程原理

    学习从小睿开始呼叫用户,然后FS怎么跟用户交互的整个流程原理;     1.小睿向欣方新发起呼叫请求;     2.欣方新可以通过线路发起SIP协议请求,来呼叫用户;     3.当用户接通后,将建立 ...

  3. Lua基本语法-lua与C#的交互(相当简单详细的例子)

    lua脚本 与 C#的交互 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) 1 Lua And C# -- ...

  4. Lua 跟 C++ 的交互

    Lua 和 C++ 是这样交互的 乱七八糟的前戏: 1. 到官网下载 Lua 文件  可參考 ->  Lua 下载与配置 2. 设置环境  可參考  ->  VS 配置Lua环境 交互过程 ...

  5. Lua与C的交互

    Lua 与 C 的交互 Lua是一个嵌入式的语言,它不仅可以是一个独立运行的程序,也可以是一个用来嵌入其它应用的程序库. C API是一个C代码与Lua进行交互的函数集,它由以下几部分构成: 1.  ...

  6. cocos2d-x lua与c++简单交互

    cocos2d-x lua与c++简单交互 version: cocos2d-x 3.6 本文讲述lua与c++的一些简单交互: lua通过消息方式调用c++无参接口 c++调用lua带参接口 1.通 ...

  7. Lua与C/C++交互问题

    初学lua,遇到注册C/C++交互函数问题 在lua与C/C++交互时,C/C++的注册Lua函数若是一个有返回类型(压栈)而不是获取类型的时候应该返回1而不是返回0,否则会出现在Lua中值为nil( ...

  8. Lua与C++的交互

    这篇文章说的挺详细的:Lua与C++的交互

  9. lua与C/C++交互概要

    转 http://blog.csdn.net/wildfireli/article/details/22307635 Lua生来就是为了和C交互的,因此使用C扩展Lua或者将Lua嵌入到C当中都是非常 ...

随机推荐

  1. disk.go

    package disk import "syscall" //空间使用结构体 type DiskStatus struct {     Size uint64     Used ...

  2. 一类SG函数递推性质的深入分析——2018ACM陕西邀请赛H题

    题目描述 定义一种有根二叉树\(T(n)\)如下: (1)\(T(1)\)是一条长度为\(p\)的链: (2)\(T(2)\)是一条长度为\(q\)的链: (3)\(T(i)\)是一棵二叉树,它的左子 ...

  3. JLOI2018 划水中...

    day -3:月考成绩刚刚出炉,嗯,还看得过去,为此,我决定脱产3天...花了一天时间,学习splay day -2:在某人(汤)的刺激下,决定用半天时间A掉去年省选D2T1,事实证明,我还是图样图森 ...

  4. 重构:以Java POI 导出EXCEL为例2

    前言 上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数.这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等. 上一篇博文地址:http://www. ...

  5. 数字证书中读取PublicKey

    1. 读取https签发证书中的key 1) 在下面的代码中,是实现读取证书字符串来读取key的,CERTIFICATE 就是一个证书的字符串, 而方法cf.generateCertificate() ...

  6. Windows上安装配置SSH教程(8)——综合应用:在Windows上使用手动方式实现SSH远程登陆与文件传输

    服务器端操作系统:Windows XP 客户端操作系统:Windows10 安装与配置顺序 1.服务端安装OpenSSH 2.服务端配置OpenSSH 3.客户端安装OpenSSH 4.客户端安装Wi ...

  7. selenium IDE工具页面介绍!

    selenium IDE工具页面,常用功能点介绍

  8. Kubernetes的DaemonSet(下篇)

    用Daemon Pod来进行通信 使用Pod来再DaemonSet中通信的手段有: 推的方式:在DaemonSet中的Pod会被配置成发送更新到如状态数据库这样的服务.这些都没有客户端. IP+端口方 ...

  9. 百度病了,必应挂了,Yandex疯了。

    前天一篇<搜索引擎百度已死>的文章火遍了互联网圈.文中作者指出如今的百度搜索首页一大半都是百度自家的产品,比如你搜索特普朗,你会发现第一页的结果分别是:百度百科.贴吧.百家号.百家号.百家 ...

  10. 服务端预渲染之Nuxt (使用篇)

    服务端预渲染之Nuxt - 使用 现在大多数开发都是基于Vue或者React开发的,能够达到快速开发的效果,也有一些不足的地方,Nuxt能够在服务端做出渲染,然后让搜索引擎在爬取数据的时候能够读到当前 ...