首页 > 编程语言 >QRust(四)示例程序

QRust(四)示例程序

时间:2024-11-11 14:31:22浏览次数:1  
标签:QRust 示例 程序 ret let pop rust pack

这一章请跟随我对QRust项目携带的demo示例讲解,逐渐熟悉并掌握QRust的使用。

无参数、无返回值的示例

先从最简单示例foo()开始。

Qt端:

void MainWindow::on_btn_foo_clicked()
{
    ui->ptext_out->appendPlainText("------------- foo() -------------");
    Rust rust("foo");
    rust.call();
 
    if (!rust.fun_ok())
    {
        ui->ptext_out->appendPlainText("Function execution failure");
        return;
    }
 
    ui->ptext_out->appendPlainText("Function execution success");
}

这是一个没有参数也没有返回值的调用,Rust rust(“foo”); 这一句在实例化调用对象的同时设置了业务函数的字符串对应名称,rust.call()发起调用,call是同步调用函数,表示调用会阻塞在这里,直到收到Rust端的返回。rust.fun_ok()来判断调用过程是否异常。

Rust端match部分:

match fun_name
{
        "foo" =>
        {
                api::foo();
                Ok(None)
        } 
        ......
}

匹配到”foo”字符串后,调用业务函数api::foo(), OK(None)表示业务函数没有返回值。

Rust端业务函数:

pub fn foo()
{
    debug!("foo() running...");
}

有1个参数,无返回值的示例

Qt端:

Rust rust("foo1");
qint32 i = 100;
QByteArray ba1 = QRust_Ser::pack_i32(i);
rust.call(ba1);

在foo1示例中传递了一个int类型参数100,QRust_Ser::pack_i32(i) 语句进行参数序列化打包,然后rust.call(ba1)将打包后的参数随调用传递给Rust。

Rust端match部分:

let a1 = de::from_pack(args.pop().unwrap())?;
api::foo1(a1);  
Ok(None)

de::from_pack反序列化得到参数100,然后调用业务函数foo1。

Rust端业务函数:

pub fn foo1(arg: i32)
{
    debug!("foo1() running... arg: {}", arg);
}

有多个参数的示例

Qt端:

Rust rust("foo2");
 
//参数 1  Parameter 1
QList<qint32> a1 = {100};
QByteArray ba1 = QRust_Ser::pack_list_i32(&a1);  //序列化 Serialization
//参数 2  Parameter 2
QHash<qint32, QString> a2 = {{1, "abcde"}};
QByteArray ba2 = QRust_Ser::pack_hi_str(&a2);  //序列化 Serialization
//参数 3  Parameter 3
QHash<QString, QString> a3 = {{"a", "12345中文"}};
QByteArray ba3 = QRust_Ser::pack_hs_str(&a3);  //序列化 Serialization
 
rust.call(ba1, ba2, ba3);
if (!rust.fun_ok())
{
        ui->ptext_out->appendPlainText("Function execution failure");
        return;
}
 
//反序列化  deserialization
QHash<QString, QString> ret;
if (!QRust_De::upack_hs_str(rust.pop(), &ret))
{
        ui->ptext_out->appendPlainText("Deserialization failure");
        return;
}

这个示例传递了三个参数,并通过QRust_De::upack_hs_str反序列化得到业务函数的返回值,rust.pop()获取并消耗第一个返回值,如果业务函数存在多个返回值,再次调用rust.pop()即可获得第二个。反序列化时需要先定义结果变量,对应是QHash<QString, QString> ret语句,然后将ret的指针传给upack_hs_str函数, upack_hs_str内部反序列化后将结果值写入ret变量。

Rust端match部分:

let a1 = de::from_pack(args.pop().unwrap())?;
let a2 = de::from_pack(args.pop().unwrap())?;
let a3 = de::from_pack(args.pop().unwrap())?;
 
let ret = api::foo2(a1, a2, a3); 
let pack = ser::to_pack(&ret)?; 
Ok(Some(pack))

分别反序列化获得三个参数,注意要和Qt端的顺序一致。业务函数的返回值通过to_pack序列化组包,然后返回给调用者。

这里需要说一下to_pack函数的参数,对于所有的集合类型和基本类型中的String和struct是传的引用(例子中是&ret),而对于其他的基本类型都是传值的,这是因为考虑到传引用的类型都是不定长的,有可能占用了很多内存,传引用可以减少内存的消耗。

返回值被封装成Ok(Some(pack))是有含义的,Ok表示函数调用成功而非异常,Some表示有值而非空。

Rust端业务函数:

pub fn foo2(arg1: Vec<i32>, arg2: HashMap<i32, String>, arg3: HashMap<String, String>) -> HashMap<String, String>
{
    debug!("foo2() running... arg1: {:?}, arg2:{:?}, args:{:?}", arg1, arg2, arg3);
    arg3
}

自定义struct示例

这是个传递自定义struct数据的示例,先看Qt端的struct定义:

struct A
{
    Q_GADGET                                    
    Q_PROPERTY(bool a MEMBER a);                
    Q_PROPERTY(QList<quint32> v MEMBER v);
public:
    bool            a;
    QList<quint32>    v;
};

第二章节对这个struct的定义有详细的说明,再看调用部分:

Rust rust("foo_struct");
 
A a1;
a1.a = true;
a1.v.append(10);
QByteArray ba1 = QRust_Ser::pack_struct(&a1);
 
quint8 count = 5;
QByteArray ba2 = QRust_Ser::pack_u8(count);
 
rust.call(ba1, ba2);
 
if (!rust.fun_ok())
{
        ui->ptext_out->appendPlainText("Function execution failure");
        return;
}
 
QHash<QString, A> rets;
if (!QRust_De::upack_hs_struct(rust.pop(), &rets))
{
        ui->ptext_out->appendPlainText("Deserialization failure");
        return;
}

可以看到在QRust中,自定义的struct的序列化和反序列化过程和前面的简单变量是一样的,并没有复杂性。

Rust端match部分:

let arg = de::from_pack(args.pop().unwrap())?;
let count = de::from_pack(args.pop().unwrap())?;
 
let ret = api::foo_struct(arg, count);
let pack = ser::to_pack(&ret)?; 
Ok(Some(pack))

Rust端业务函数:

pub fn foo_struct(arg: A, count: u8) -> HashMap<String, A>
{
    debug!("foo_struct() runnint... count: {}, arg: {:#?}", count, arg);
    let mut v = HashMap::new();
    for i in 0..count
    {
        v.insert(i.to_string(), arg.clone());
    }
    v
}

多个参数、多个返回值的示例

Qt端:

QList<bool>     b   = {true, false};
QList<qint8>    i8  =   {-1, 0, 1};
QList<qint16>   i16 =   {-1, 0, 1};
QList<qint32>   i32 =   {-1, 0, 1};
QList<qint64>   i64 =   {-1, 0, 1};
QList<quint8>   u8  =   {0, 1, 2};
QList<quint16>  u16 =   {0, 1, 2};
QList<quint32>  u32 =   {0, 1, 2};
QList<quint64>  u64 =   {0, 1, 2};
QList<float>    f32 =   {(float)0.1, (float)0.2, (float)0.3};
QList<double>   f64 =   {0.1, 0.2, 0.3};
QList<QString>  str =   {"abcde", "12345", "中文"};
QList<A>        strcts;
A a1;
a1.a = true;
a1.v.append(10);
strcts.append(a1);
 
QByteArray ba1 = QRust_Ser::pack_list_bool(&b);
QByteArray ba2 = QRust_Ser::pack_list_i8(&i8);
QByteArray ba3 = QRust_Ser::pack_list_i16(&i16);
QByteArray ba4 = QRust_Ser::pack_list_i32(&i32);
QByteArray ba5 = QRust_Ser::pack_list_i64(&i64);
QByteArray ba6 = QRust_Ser::pack_list_u8(&u8);
QByteArray ba7 = QRust_Ser::pack_list_u16(&u16);
QByteArray ba8 = QRust_Ser::pack_list_u32(&u32);
QByteArray ba9 = QRust_Ser::pack_list_u64(&u64);
QByteArray ba10= QRust_Ser::pack_list_f32(&f32);
QByteArray ba11= QRust_Ser::pack_list_f64(&f64);
QByteArray ba12= QRust_Ser::pack_list_str(&str);
QByteArray ba13= QRust_Ser::pack_list_struct(&strcts);
 
Rust rust("test_list");
rust.call(ba1, ba2, ba3, ba4, ba5, ba6, ba7, ba8, ba9, ba10, ba11, ba12, ba13);
 
QList<bool>     ret_b;
QList<qint8>    ret_i8;
QList<qint16>   ret_i16;
QList<qint32>   ret_i32;
QList<qint64>   ret_i64;
QList<quint8>   ret_u8;
QList<quint16>  ret_u16;
QList<quint32>  ret_u32;
QList<quint64>  ret_u64;
QList<float>    ret_f32;
QList<double>   ret_f64;
QList<QString>  ret_str;
QList<A>        ret_strcts;
QRust_De::upack_list_bool(rust.pop(), &ret_b);
QRust_De::upack_list_i8(rust.pop(), &ret_i8);
QRust_De::upack_list_i16(rust.pop(), &ret_i16);
QRust_De::upack_list_i32(rust.pop(), &ret_i32);
QRust_De::upack_list_i64(rust.pop(), &ret_i64);
QRust_De::upack_list_u8(rust.pop(), &ret_u8);
QRust_De::upack_list_u16(rust.pop(), &ret_u16);
QRust_De::upack_list_u32(rust.pop(), &ret_u32);
QRust_De::upack_list_u64(rust.pop(), &ret_u64);
QRust_De::upack_list_f32(rust.pop(), &ret_f32);
QRust_De::upack_list_f64(rust.pop(), &ret_f64);
QRust_De::upack_list_str(rust.pop(), &ret_str);
QRust_De::upack_list_struct(rust.pop(), &ret_strcts);

这个示例是对所有List类型的单元测试,将所有list类型参数传入和传回。

Rust端match部分:

let b = de::from_pack(args.pop().unwrap())?;
let i8 = de::from_pack(args.pop().unwrap())?;
let i16 = de::from_pack(args.pop().unwrap())?;
let i32 = de::from_pack(args.pop().unwrap())?;
let i64 = de::from_pack(args.pop().unwrap())?;
let u8 = de::from_pack(args.pop().unwrap())?;
let u16 = de::from_pack(args.pop().unwrap())?;
let u32 = de::from_pack(args.pop().unwrap())?;
let u64 = de::from_pack(args.pop().unwrap())?;
let f32 = de::from_pack(args.pop().unwrap())?;
let f64 = de::from_pack(args.pop().unwrap())?;
let str = de::from_pack(args.pop().unwrap())?;
let strct = de::from_pack(args.pop().unwrap())?;
 
let (b, i8, i16, i32, i64, 
        u8, u16, u32, u64, 
        f32, f64, str, strct) 
        = api::test_list(b, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, str, strct);
 
let mut pack = Vec::new();
pack.extend(ser::to_pack(&b)?);
pack.extend(ser::to_pack(&i8)?);
pack.extend(ser::to_pack(&i16)?);
pack.extend(ser::to_pack(&i32)?);
pack.extend(ser::to_pack(&i64)?);
pack.extend(ser::to_pack(&u8)?);
pack.extend(ser::to_pack(&u16)?);
pack.extend(ser::to_pack(&u32)?);
pack.extend(ser::to_pack(&u64)?);
pack.extend(ser::to_pack(&f32)?);
pack.extend(ser::to_pack(&f64)?);
pack.extend(ser::to_pack(&str)?);
pack.extend(ser::to_pack(&strct)?);
let pack_multi = ser::to_pack_multi(pack);
Ok(Some(pack_multi))

多返回值时,每个返回打包后依次添加(extend)到多返回值的集合中,ser::to_pack_multi函数将集合再打包返回。

Rust端业务函数:

pub fn test_list(b:Vec<bool>, i8:Vec<i8>, i16:Vec<i16>, i32:Vec<i32>, i64:Vec<i64>, 
    u8:Vec<u8>, u16:Vec<u16>, u32:Vec<u32>, u64:Vec<u64>, f32:Vec<f32>, f64:Vec<f64>, 
    str:Vec<String>, strct: Vec<A>) 
    -> (Vec<bool>, Vec<i8>, Vec<i16>, Vec<i32>, Vec<i64>, 
        Vec<u8>, Vec<u16>, Vec<u32>, Vec<u64>, Vec<f32>, Vec<f64>, 
        Vec<String>, Vec<A>) 
{
    debug!("b: {:?}", b);
    debug!("i8: {:?}", i8);
    debug!("i16: {:?}", i16);
    debug!("i32: {:?}", i32);
    debug!("i64: {:?}", i64);
    debug!("u8: {:?}", u8);
    debug!("u16: {:?}", u16);
    debug!("u32: {:?}", u32);
    debug!("u64: {:?}", u64);
    debug!("f32: {:?}", f32);
    debug!("f64: {:?}", f64);
    debug!("str: {:?}", str);
    debug!("strct: {:#?}", strct);
 
    (b, i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, str, strct)
}

异步调用示例

如果业务函数执行时间较长,同步调用将会阻塞Qt的窗口线程,造成窗口程序无响应。在QRust中异步调用是通过Qt的信号/槽机制来运行的。

Qt端mainwindow.h文件:

private:
    Ui::MainWindow *ui;
 
    Rust *rust_asyn;

先定义一个Rust变量指针,异步调用时要用到它。

再定义一个调用完成的槽函数:

private slots:
    void finish();

然后在构造函数中进行信号和槽的绑定(mainwindow.cpp文件):

rust_asyn = new Rust();         
connect(rust_asyn, &Rust::fun_finish, this, &MainWindow::finish);

new Rust()实例化Rust对象,注意这里没有定义要调用的函数字符串名。

我们再看调用时的代码:

rust_asyn->reset();
rust_asyn->setFunName("test_asyn");
qint32 i = 7;
QByteArray ba1 = QRust_Ser::pack_i32(i);
rust_asyn->asyncall(ba1);

同步调用时Rust对象随调用完成就可以释放了,而异步调用时rust对象因为信号和槽的原因,通常生命周期比较长,因此这里的写法和前面是有区别的:

  • reset()会重置rust对象,这意味着rust对象是可以重复使用的。
  • setFunName设置调用的业务函数名称。
  • asyncall说明这是一次异步调用,asyncall函数是立即返回的,因此不占用GUI线程。

QRust内部会监视调用,如果得到了Rust的返回,立即发出信号,槽函数finish即刻执行:

void MainWindow::finish()
{
    ui->btn_asyn->setEnabled(true);    //enable button
    ui->btn_asyn->setStyleSheet("Color:black");
    if (!rust_asyn->fun_ok())
    {
        ui->ptext_out->appendPlainText("Function execution failure");
        return;
    }
 
    qint32 ret;
    if (!QRust_De::upack_i32(rust_asyn->pop(), &ret))
    {
        ui->ptext_out->appendPlainText("Deserialization failure");
        return;
    }
 
    QString out = QString("result: %1").arg(QString::number(ret));
    ui->ptext_out->appendPlainText(out);
}

异步调用是Qt端的技术,Rust端不关心也无法区分调用是同步还是异步的。

Rust端match部分:

let i = de::from_pack(args.pop().unwrap())?;
let ret = api::test_asyn(i);
let pack = ser::to_pack(&ret)?;
Ok(Some(pack))

Rust端业务函数:

pub fn test_asyn(i: i32) -> i32
{
    debug!("test_asyn() start...");
    //休眠10秒  
    let ten_secs = time::Duration::from_millis(10_000);
    thread::sleep(ten_secs);
 
    debug!("test_asyn() finish...");
    i + i
}

性能测试示例

这个示例通过10万次的调用,计算 1 + 2 + 3 + …… + 100000 的值。

Qt端:

quint64 n = 0;
int max = 100000;
 
ui->ptext_out->appendPlainText("------------- test speed -------------");
qint64 start = QDateTime::currentMSecsSinceEpoch();
for (int i = 1; i <= max; i++)
{
        Rust rust("test_speed");
        QByteArray ba1 = QRust_Ser::pack_u64(n);
        QByteArray ba2 = QRust_Ser::pack_i32(i);
        rust.call(ba1, ba2);
 
        if (!rust.fun_ok())
        {
                ui->ptext_out->appendPlainText("Function execution failure");
                return;
        }
 
        if (!QRust_De::upack_u64(rust.pop(), &n))
        {
                ui->ptext_out->appendPlainText("Deserialization failure");
                return;
        }
}
qint64 end = QDateTime::currentMSecsSinceEpoch();
 
QString out1 = QString("1 + 2 + 3 + ... + %1 = %2").arg(QString::number(max), QString::number(n));
QString out2 = QString("time: %1ms").arg(QString::number(end-start));
ui->ptext_out->appendPlainText(out1);
ui->ptext_out->appendPlainText(out2);

Rust的match部分:

let n = de::from_pack(args.pop().unwrap())?;
let i = de::from_pack(args.pop().unwrap())?;
let n = test_speed(n, i);
let pack = ser::to_pack(&n)?;
Ok(Some(pack))

Rust端业务函数:

pub fn test_speed(n: u64, i: i32) -> u64
{
    n + i as u64
}

下面是在我的笔记本上运行性能测试示例的截图,10万次调用消耗1秒多时间,可以看到QRust效率还是很高的。实际上时间消耗主要发生在调用过程中的序列化和反序列化操作上。

This image has an empty alt attribute; its file name is demo_speed.png

项目中其他示例,主要目的是进行全体数据类型的单元测试。

名称中带有empty的示例是在做所有集合类型的空集合测试,因为空集合在序列化和反序列化操作中有特殊性。

标签:QRust,示例,程序,ret,let,pop,rust,pack
From: https://www.cnblogs.com/dyf029/p/18539628

相关文章

  • 未能加载文件或程序集“Microsoft.Xaml.Behaviors”或它的某一个依赖项
    autocadwpfcommunitytoolkit.mvvmcombobox选定项变化事件安装Microsoft.Xaml.Behaviors.Wpf包使用命名空间xmlns:i="http://schemas.microsoft.com/xaml/behaviors"那么comobox应该是<ComboBoxItemsSource="{BindingItems}"><i:Interaction.Trigger......
  • 使用 .NET Core 7 和 SignalR 构建实时聊天应用程序
    动动你可爱的小手,请关注下本人公众号,继续为您提供更多原创技术文章。谢谢给为的支持SignalR 是一个ASP.NET库,它支持实时Web功能,允许服务器立即将更新推送到客户端,从而提高应用程序的效率和响应能力。它通过简化通信和支持各种传输协议,对于聊天应用程序、实时仪表板和协......
  • QRust(三)编程框架
    把Rust作为动态库或静态库链接到Qt环境中,本就是一件复杂的工作,在此基础上还要引入QRust更是难上加难,因此在这一章我将手把手的引导你向前迈进,并跨过我曾经遇到的坑。编程环境Qt环境:Qt6,没错不支持Qt5。因为我发现struct的类型推导在Qt5环境下有错误。Rust环境:理论上没有限制,但在......
  • windows11 安装驱动无法验证此设备所需的驱动程序的数字签名。
    参考 Win11和Win10怎么禁用驱动程序强制签名?关闭Windows系统驱动强制签名的技巧?-知乎参考  windows10该值受安全引导策略保护,无法进行修改或删除。禁用驱动程序强制签名_该值受安全引导策略保护无法进行修改或删除-CSDN博客 什么是驱动程序签名?驱动程序签名又叫做驱动......
  • QRust(二)数据类型
    QRust支持的数据类型可分为两类:基本类型、集合类型。这些数据类型可作为函数参数、返回值或struct的字段,在Qt和Rust之间传递。基本类型Rust端Qt端boolbooli8qint8i16qint16i32qint32i64qint64u8quint8u16quint16u32quint32u64quint64......
  • 基于springboot+vue.js+uniapp小程序的企业资产管理系统附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图核心技术介绍后端框架SpringBoot前端框架Vue持久层框架MyBaits为什么选择我代码参考数据库参考测试用例参考源码获取前言......
  • 基于springboot+vue.js+uniapp小程序的华强北商城二手手机管理系统附带文章源码部署视
    文章目录前言详细视频演示具体实现截图核心技术介绍后端框架SpringBoot前端框架Vue持久层框架MyBaits为什么选择我代码参考数据库参考测试用例参考源码获取前言......
  • 基于springboot+vue.js+uniapp小程序的汽车资讯网站附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图核心技术介绍后端框架SpringBoot前端框架Vue持久层框架MyBaits为什么选择我代码参考数据库参考测试用例参考源码获取前言......
  • 程序员如何借势AI提高自己:从高效工作到技能升级的全面指南
    又是一年1024,时光荏苒,转眼又到了这个特别的日子。坦白说,这篇文章我其实并不太想写,因为我并没有通过AI找到普适于程序员群体的高效赚钱秘籍。然而,反思过去的工作,我发现利用AI的确让我在工作中变得更加灵活,也因此有了更多时间去思考其他问题。因此,我希望能够分享一些我在使用AI过程......
  • rstrui.exe 是 Windows 系统中的系统还原程序,全名为 "System Restore User Interface"
    rstrui.exe是Windows系统中的系统还原程序,全名为"SystemRestoreUserInterface"。它是Windows操作系统的一部分,允许用户通过图形界面启动系统还原功能,以恢复计算机到先前的状态。以下是关于rstrui.exe的详细解释:1. 功能系统还原:rstrui.exe 负责启动系统还原向导,帮......