• Qt、C/C++环境中内嵌LUA脚本、实现LUA函数的调用执行


    C/C++中内嵌LUA脚本、实现LUA函数的调用执行



    1、LUA简介

    LUA是一个脚本语言,由标准C编写而成,几乎在所有操作系统和平台上都可以编译、运行,可以很方便的嵌入到其他C/C++环境中,如Qt、VS2012等。

    一般的lua脚本文件的后缀为.lua,移植lua源码异常的简单,下载源码包后,直接编译即可,因为上面我们说过了LUA是用标准C编写的,所以几乎你常见的编程环境它都能编译LUA。

    LUA源码下载地址:http://www.lua.org/ftp/

    下载最新版5.4.4版本,解压缩源码如下:

    将以上这些文件除了lua.c和luac.c加入到你的开发环境中进行编译,就算移植完成了!

    lua.c和luac.c中都有main函数,需要分别编译这两个我文件,其中,lua.c编译出来是解析器,luac.c编译出来是编译器。


    2、LUA脚本的解释器和编译器

    Q:什么是解释器和编译器?

    A:

    解释器:顾名思义,就是对LUA进行解释说明,能够认识出LUA脚本并运行

    编译器:对LUA脚本文件进行编译,生成可以供解释器运行的LUA可执行程序

    我使用lua源码包版本5.4.4在windows下编译出了可执行文件,其中:lua.exe为解释器,luac.exe为编译器。

    下载地址和使用方法见:https://gitee.com/yzhengBTT/lua-windows


    3、C环境中内嵌LUA执行LUA函数调用

    这里我使用的C开发环境是:Eclipse C/C++配合MinGW。

    移植很简单的,将lua源码除了lua.c和luac.c之外,其他源码加入到工程即可。

    示例代码:

    /*
     * main.c
     *
     *  Created on: 2022年8月25日
     *      Author: hello
     */
    
    #include "./lua-5.4.4/lua.h"
    #include "./lua-5.4.4/lualib.h"
    #include "./lua-5.4.4/lauxlib.h"
    
    int clua_add(lua_State* L, int a, int b)
    {
    	int sum = 0;
    
    	/* 函数入栈 */
    	lua_getglobal(L, "add");
    
    	/* 第一个函数参数入栈 */
    	lua_pushnumber(L, a);
    
    	/* 第二个函数参数入栈 */
    	lua_pushnumber(L, b);
    
    	/* 执行函数调用。2表示有两个函数形参,1表示add函数只有一个返回值,调用lua_call函数后lua自动出栈参数和函数,并将函数的执行结果入栈 */
    
    	/*
    	 * 执行函数调用
    	 * 2表示lua脚本中add函数需要输入两个函数参数
    	 * 1表示lua脚本中add函数有一个返回值
    	 * 执行完函数调用后,lua自动出栈函数和参数
    	 */
    	lua_call(L, 2, 1);
    
    	/*
    	 * 得到函数add函数执行结果
    	 * -1表示最后一个返回值,因为lua的函数可以返回多个值的。
    	 */
    	sum = lua_tonumber(L, -1);
    
    	/* 出栈一个数据。此时栈中存的是add函数的执行结果,所以需要出栈 */
    	lua_pop(L, 1);
    
    	return sum;
    }
    
    int main(int argc, char* argv[])
    {
    	int sum = 0;
    
    	lua_State* L;
    
    	L = luaL_newstate();  /* 创建一个句柄 */
    
    	luaL_openlibs(L);     /* 打开lua库 */
    
    #if 0
    	if(luaL_dofile(L, "./mylua.lua"))  /* 从lua脚本文件 中加载lua脚本语句 */
    	{
    		printf(" 加载LUA文件失败! \r\n");
    		return -1;
    	}
    #endif
    
    	if(luaL_dostring(L, (const char *)"function add(a, b) return a + b end"))  /* 从字符串中加载lua脚本语句 */
    	{
    		printf(" LUA语句有误!\r\n");
    		return -1;
    	}
    
    	sum = clua_add(L, 10, 20);
    	printf(" sum = %d \r\n", sum);
    
    	if(luaL_dostring(L, (const char *)"function add(a, b) return a + b + 100 end"))  /* 从字符串中加载lua脚本语句 */
    	{
    		printf(" LUA语句有误!\r\n");
    		return -2;
    	}
    
    	sum = clua_add(L, 10, 20);
    	printf(" sum = %d \r\n", sum);
    
    	lua_close(L);  /* 关闭lua,清理内存 */
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    4、Qt内嵌LUA执行LUA函数调用

    移植很简单的,将lua源码文件除了lua.c和luac.c之外,加入到Qt工程即可。

    我为了方便管理,将lua源码放到了一个目录里,然后放到mainwindow.cpp同文件夹下:

    在Qt工程上右键->添加已经存在的目录->选择lua-5.4.4即可将源码加入到Qt工程。

    然后添加头文件包含:#include "./lua-5.4.4/lua.hpp"

    然后编译工程即可。

    下面是示例程序,程序比较简单,实现两数相加,单击按钮后解析lua脚本中add函数,进行两数相加操作:

    点击按钮槽函数如下:

    void MainWindow::on_pushButton_clicked()
    {
        int a = ui->lineEditA->text().toInt();
        int b = ui->lineEditB->text().toInt();
        int sum = 0;
    
        lua_State* L = this->lua_state;
    
        const char* luastring = ui->plainTextEdit->toPlainText().toStdString().c_str();
    
        if(luaL_dostring(L, luastring))  /* 从字符串中加载LUA脚本 */
        {
            qDebug() << "LUA脚本有误!";
            return;
        }
    
        /* 函数入栈 */
        lua_getglobal(L, "add");
    
        /* 第一个函数参数入栈 */
        lua_pushnumber(L, a);
    
        /* 第二个函数参数入栈 */
        lua_pushnumber(L, b);
    
        /*
         * 执行函数调用
         * 2表示lua脚本中add函数需要输入两个函数参数
         * 1表示lua脚本中add函数有一个返回值
         * 执行完函数调用后,lua自动出栈函数和参数
         */
        lua_call(L, 2, 1);
    
        /*
         * 得到add函数执行结果
         * -1表示最后一个返回值,因为lua的函数可以返回多个值的。
         */
        sum = lua_tonumber(L, -1);
    
        /* 出栈一个数据。此时栈中存的是add函数的执行结果,所以需要出栈 */
        lua_pop(L, 1);
    
        ui->lineEditResult->setText(QString::asprintf("%d", sum));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    现在我们修改LUA脚本变成a+b+100,再点击执行:

    这样就实现了动态的解析LUA脚本供C/C++环境使用。


    5、内嵌LUA脚本在实际项目中的案例应用

    上面已经介绍了LUA脚本及内嵌入C/C++环境,那么实际的使用场景是怎样的呢?

    下面就举一个实际的使用案例。

    现在有一个嵌入式相关项目,使用Qt做一款上位机软件,通过串口和下位机进行通信,通信协议为modbus,下位机为各类485型传感器。

    目前有一个温度传感器需要接入上位机、一个水浸入检测传感器需要接入;

    水浸传感器是开关量传感器,只有0和1两种状态;

    温度传感器是数字量传感器,厂家为了迎合modbus协议并且为了数据好处理,将浮点格式的温度值扩大了10倍进行传输,例如28.5度扩大10倍是285,将285通过modbus进行传输。

    那么现在问题就来了,温度传感器和水浸传感器虽然通信协议是一致的,上位机通过modbus接收到传感器数据后:

    如果是温度传感器的,那么需要除以10倍才能得到正确的温度值;

    但是如果是水浸传感器就不用除以10倍;

    而此时如果还有一个VOC传感器需要接入,并且上位机需要将读取到的VOC数值进行一个复杂的公式转换成NMHC后在显示;

    这时候你就会发现,每一个传感器都有不一样的最终值计算方式,难道要将所有传感器的计算格式方式都包含进上位机中吗?

    简单的加减乘除还可以包含进上位机中,如果是VOC转NMHC这种复杂计算公式的,你怎么包含进去呢?

    所以,这就体现了LUA的用处!

    办法如下:

    在上位机中嵌入LUA,编写一个mylua.lua脚本文件,里面就一个getValue函数。

    当上位机接收到传感器数值时,通过LUA提供的API函数,将该数值传入getValue.lua脚本中的getValue函数,然后在获得getValue的返回值,

    而在getValue中实现了数值的转换或计算,由于getValue.lua脚本文件是独立于上位机的,可以随便更改函数体,当传感器是水浸时,getValue直接返回输入的值即可,当传感器是温度时,getValue返回输入的值除以10即可;

    所以这样就实现了上位机针对不同传感器的数据处理!


    end…

  • 相关阅读:
    UE5 虚幻引擎中UI、HUD和UMG的区别与联系
    线扫相机DALSA--常见问题四:修改相机参数,参数保存无效情况
    达梦控制台还原报错“管道失败”
    应力奇异,你是一个神奇的应力!
    专利交底书怎么写 -
    数字电路基础02(用2选1MUX实现与、或、非、与非、或非、异或、同或)
    STM32串口DMA+空闲中断接收数据-基于CubeMX创建
    使用 BeanUtils.copyProperties属性拷贝
    使用client-go实现自定义控制器
    day16-测试自动化之selenium的PO模式
  • 原文地址:https://blog.csdn.net/qq153471503/article/details/126536177