这一章请跟随我对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效率还是很高的。实际上时间消耗主要发生在调用过程中的序列化和反序列化操作上。
项目中其他示例,主要目的是进行全体数据类型的单元测试。
名称中带有empty的示例是在做所有集合类型的空集合测试,因为空集合在序列化和反序列化操作中有特殊性。
标签:QRust,示例,程序,ret,let,pop,rust,pack From: https://www.cnblogs.com/dyf029/p/18539628