Lua调用C函数 -- Qt实现

Lua调用C函数的两种Qt实现
本文介绍通过Qt实现Lua调用C函数的两种方法:一是将C函数作为应用的一部分直接调用;二是将C函数封装为动态链接库,通过Lua调用库中的API。文章详细展示了代码配置与实现流程。

Lua调用C函数 – Qt实现

第一种方式:C函数作为应用程序的一部分,使用lua来调用

流程:

1.应用程序加载Lua脚本
2.Lua脚本中调用C函数中

程序:

luaCallC.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-12-18T14:24:19
#
#-------------------------------------------------

QT       += core

QT       -= gui

TARGET = luaCallC
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += main.cpp

HEADERS += \
    lauxlib.h \
    lua.h \
    lua.hpp \
    luaconf.h \
    lualib.h
LIBS += -L$$PWD -llua53

main.cpp
#include <QCoreApplication>

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>

using namespace std;
//待Lua调用的C注册函数。
static int add2(lua_State* L)
{
    //检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推。
    //如果Lua代码在调用时传递的参数不为number,该函数将报错并终止程序的执行。
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    //将函数的结果压入栈中。如果有多个返回值,可以在这里多次压入栈中。
    lua_pushnumber(L,op1 + op2);
    //返回值用于提示该C函数的返回值数量,即压入栈中的返回值数量。
    return 1;
}

//另一个待Lua调用的C注册函数。
static int sub2(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 - op2);
    return 1;
}

int main()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    //将指定的函数注册为Lua的全局函数变量,其中第一个字符串参数为Lua代码
    //在调用C函数时使用的全局函数名,第二个参数为实际C函数的指针。
    lua_register(L, "add2", add2); //C函数作为应用程序的一部分
    lua_register(L, "sub2", sub2);
    //在注册完所有的C函数之后,即可在Lua的代码块中使用这些已经注册的C函数了。

    if(luaL_dofile(L,"base.lua")){
        const char *error = lua_tostring(L, -1);
        std::cout << error <<std::endl;
        return false;
    }
    lua_close(L);
    return 0;
}

require "mylib"  --指定包名称
--在调用时,必须是package.function
print(mylib.add(1.0,2.0))
print(mylib.sub(20.1,19))

第二种方式:C函数作为动态链接库,使用lua来调用

流程:

1.使用QT生成共享库
2.lua调用dll,调用对应api

程序:

mylib.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-12-18T12:02:24
#
#-------------------------------------------------

QT       -= core gui

TARGET = mylib
TEMPLATE = lib

DEFINES += MYLIB_LIBRARY

SOURCES += mylib.cpp

HEADERS += mylib.h \
    lauxlib.h \
    lua.h \
    lua.hpp \
    luaconf.h \
    lualib.h

LIBS += -L$$PWD -llua53

unix {
    target.path = /usr/lib
    INSTALLS += target
}

DISTFILES += \
    lua53.dll

mylib.h
#ifndef MYLIB_H
#define MYLIB_H

#if defined(MYLIB_LIBRARY)
#  define MYLIBSHARED_EXPORT Q_DECL_EXPORT
#else
#  define MYLIBSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // MYLIB_H

mylib.cpp
#include "mylib.h"
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>

//待注册的C函数,该函数的声明形式在上面的例子中已经给出。
//需要说明的是,该函数必须以C的形式被导出,因此extern "C"是必须的。
//函数代码和上例相同,这里不再赘述。
extern "C" int add(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 + op2);
    return 1;
}

extern "C" int sub(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 - op2);
    return 1;
}

//luaL_Reg结构体的第一个字段为字符串,在注册时用于通知Lua该函数的名字。
//第一个字段为C函数指针。
//结构体数组中的最后一个元素的两个字段均为NULL,用于提示Lua注册函数已经到达数组的末尾。
static luaL_Reg mylibs[] = {
    {"add", add},
    {"sub", sub},
    {NULL, NULL}
};

//该C库的唯一入口函数。其函数签名等同于上面的注册函数。见如下几点说明:
//1. 我们可以将该函数简单的理解为模块的工厂函数。
//2. 其函数名必须为luaopen_xxx,其中xxx表示library名称。Lua代码require "xxx"需要与之对应。
//3. 在luaL_register的调用中,其第一个字符串参数为模块名"xxx",第二个参数为待注册函数的数组。
//4. 需要强调的是,所有需要用到"xxx"的代码,不论C还是Lua,都必须保持一致,这是Lua的约定,
//   否则将无法调用。
extern "C" __declspec(dllexport)
int luaopen_mylib(lua_State* L)
{
    luaL_newlib(L, mylibs); //c函数作为库供lua调用,使用luaL_newlib
    return 1;
}

require "mylib"  --指定包名称
--在调用时,必须是package.function
print(mylib.add(1.0,2.0))
print(mylib.sub(20.1,19))
<think>我们讨论的是在C语言中调用Qt串口发送函数的方法。由于Qt是用C++编写的,而C语言与C++在函数调用和对象处理上有根本区别(C不支持面向对象特性),因此直接调用存在技术障碍。以下是两种可行的跨语言调用方案: --- ### 方案一:使用C++封装层(推荐) 通过C++创建Qt串口对象,并导出C风格接口供C语言调用。 #### 步骤实现: 1. **创建C++封装类**(继承`QObject`) ```cpp // SerialPortWrapper.h #ifdef __cplusplus extern "C" { #endif // C语言兼容接口声明 void* create_serial_port(); void send_serial_data(void* port, const char* data, int length); #ifdef __cplusplus } #endif ``` 2. **实现封装类**(.cpp文件) ```cpp // SerialPortWrapper.cpp #include "SerialPortWrapper.h" #include <QSerialPort> void* create_serial_port() { QSerialPort* port = new QSerialPort; port->setPortName("COM1"); port->open(QIODevice::ReadWrite); return static_cast<void*>(port); } void send_serial_data(void* port, const char* data, int length) { QSerialPort* serial = static_cast<QSerialPort*>(port); serial->write(data, length); } ``` 3. **C语言调用示例** ```c // main.c #include "SerialPortWrapper.h" int main() { void* port = create_serial_port(); char data[] = "Hello from C"; send_serial_data(port, data, sizeof(data)-1); return 0; } ``` **关键技术点**: - `extern "C"`:防止C++编译器对函数名进行修饰 - `void*`类型:作为对象指针的通用传递载体 - 内存管理:需额外提供`destroy_serial_port()`函数释放对象[^1] --- ### 方案二:基于回调函数Lua脚本集成(动态扩展) 适用于需动态调整发送逻辑的场景,通过Lua脚本桥接C/C++和Qt。 ```mermaid graph LR C[主程序: C语言] --> |调用Lua API| L[Lua脚本] L --> |调用注册函数| Q[Qt串口发送函数] ``` 1. **在Qt中注册C++函数Lua** ```cpp // Lua注册函数 lua_register(L, "qtSerialSend", lua_SerialSend); // Lua调用的发送函数实现 static int lua_SerialSend(lua_State* L) { const char* data = luaL_checkstring(L, 1); QSerialPort port; port.write(data); return 0; } ``` 2. **C语言通过Lua执行发送** ```c // C中调用Lua脚本 lua_State* L = luaL_newstate(); luaL_dofile(L, "send_script.lua"); // 脚本内容: qtSerialSend("C->Lua->Qt") ``` **优势**:支持运行时修改发送逻辑(如引用[^4]中的传感器数据处理场景)[^4] --- ### 关键挑战与解决方案 | 挑战 | 解决方案 | |------|----------| | C无法直接操作C++对象 | 通过`void*`类型中转对象指针 | | Qt事件循环依赖 | 确保在主线程调用或使用`QCoreApplication::processEvents()` | | 内存泄漏风险 | 显式提供资源释放接口 | | 线程安全问题 | 使用Qt的信号槽进行跨线程通信 | --- ### 完整跨语言调用流程 1. **初始化Qt应用**(必须存在`QCoreApplication`实例) ```cpp int main(int argc, char* argv[]) { QCoreApplication app(argc, argv); // 必须创建 // ... 调用C入口函数 } ``` 2. **编译链接注意事项** - C代码需用C编译器处理 - C++代码用C++编译器处理 - 最终链接时包含Qt库(`-lQt5SerialPort`) --- ### 性能优化建议 1. **批量发送**:合并多次小数据包发送 2. **异步处理**:通过信号槽非阻塞调用 3. **缓冲区管理**:预分配固定大小发送缓冲区 > **注意**:直接跨语言调用会带来约15-30%的性能损耗,高频通信场景建议采用纯C++实现[^1][^3] §§相关问题§§ 1. C语言和C++混合编程有哪些注意事项? 2. 如何保证Qt对象在跨语言调用时的线程安全? 3. Lua脚本在嵌入式通信中有哪些典型应用场景? </think>### 在C语言中调用Qt串口发送函数实现方法 由于Qt是C++框架,而C语言无法直接操作Qt对象,需通过**桥接设计**实现跨语言调用。以下是具体实现方案: --- ### 方案一:封装C接口(推荐) 通过C++创建Qt串口对象并导出C风格接口供C语言调用: ```cpp // 桥接头文件 serial_bridge.h #ifdef __cplusplus extern "C" { #endif // C语言可调用的接口 void* create_serial(const char* portName); int send_serial_data(void* handle, const char* data, int length); void close_serial(void* handle); #ifdef __cplusplus } #endif ``` ```cpp // 实现文件 serial_bridge.cpp #include "serial_bridge.h" #include <QSerialPort> void* create_serial(const char* portName) { QSerialPort* serial = new QSerialPort(portName); serial->setBaudRate(QSerialPort::Baud115200); if (!serial->open(QIODevice::ReadWrite)) { delete serial; return nullptr; } return static_cast<void*>(serial); // 返回不透明指针 } int send_serial_data(void* handle, const char* data, int length) { QSerialPort* serial = static_cast<QSerialPort*>(handle); qint64 bytesWritten = serial->write(data, length); serial->flush(); // 确保数据发送 return (bytesWritten == length) ? 0 : -1; // 返回发送状态 } void close_serial(void* handle) { QSerialPort* serial = static_cast<QSerialPort*>(handle); serial->close(); delete serial; } ``` **C语言调用示例**: ```c #include "serial_bridge.h" int main() { void* serial = create_serial("/dev/ttyUSB0"); if (!serial) return -1; const char data[] = {0x01, 0x02, 0x03}; int result = send_serial_data(serial, data, sizeof(data)); close_serial(serial); return result; } ``` **关键技术点**: 1. **不透明指针**:使用`void*`隐藏C++对象细节 2. **内存管理**:C++显式管理Qt对象生命周期 3. **错误处理**:返回状态码兼容C语言错误处理习惯[^1] --- ### 方案二:回调机制 + Lua扩展 结合引用[^4]的Lua脚本方案实现动态逻辑: ```mermaid graph LR C[主程序: C语言] -->|调用| Lua[Lua虚拟机] Lua -->|回调| Bridge[桥接模块] Bridge -->|调用| Qt[Qt串口对象] ``` 1. **在Qt中注册发送函数Lua**: ```cpp // Qt端注册Lua函数 lua_register(L, "qtSerialSend", [](lua_State* L) -> int { const char* data = lua_tostring(L, 1); // 获取Lua参数 QSerialPort::sendData(data); // 实际发送 return 0; }); ``` 2. **C语言通过Lua调用**: ```c // C语言调用Lua脚本 lua_State* L = luaL_newstate(); luaL_dofile(L, "send_script.lua"); // 脚本内容: qtSerialSend("DATA") ``` --- ### 关键挑战与解决方案 | 挑战 | 解决方案 | |------|----------| | 对象生命周期管理 | 显式提供create/destroy接口 | | 线程安全 | 使用Qt信号槽跨线程传递数据 | | 数据类型转换 | 二进制数据用`char*/length`传递 | | 事件循环依赖 | 在主线程创建Qt对象[^3] | --- ### 完整调用流程 1. **初始化Qt环境**(必需): ```cpp QCoreApplication app(argc, argv); // 创建事件循环 ``` 2. **编译注意事项**: - C文件用C编译器处理 - C++文件用C++编译器处理 - 链接时包含Qt库:`-lQt5SerialPort` > **性能提示**:高频通信场景建议使用方案一,动态逻辑场景推荐方案二[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值