在嵌入式系統(tǒng)中集成 Rust 和 Qt 的實(shí)踐
出處:維庫(kù)電子市場(chǎng)網(wǎng) 發(fā)布于:2023-02-09 16:13:26
將兩者整合在一起時(shí),挑戰(zhàn)就來了。如果沒有仔細(xì)的計(jì)劃和努力,組合的 Rust-Qt 應(yīng)用程序就會(huì)成為這兩種語言弱屬性的犧牲品。Qt/C++對(duì)Rust的調(diào)用可能不安全,域間可能會(huì)出現(xiàn)并發(fā)問題,Rust的高性能和可移植性可能會(huì)打折扣。
本文介紹了為嵌入式應(yīng)用程序?qū)崿F(xiàn) Rust 和 Qt 世界的推薦原則。
Rust 和 Qt 的背景
Rust 擁有豐富的庫(kù)生態(tài)系統(tǒng),用于序列化和反序列化、異步操作、解析不安全輸入、線程、靜態(tài)分析等,而 Qt 是一個(gè) C++ 工具包,支持跨各種平臺(tái)的豐富的、基于 GUI 的應(yīng)用程序,從 iOS 到嵌入式Linux。Qt 應(yīng)用程序包括表示業(yè)務(wù)邏輯的 C++ 插件、定義 GUI 組件和布局的 QML 以及用于 GUI 腳本的 JavaScript。
將 Rust 庫(kù)集成到 Qt 框架中,為開發(fā)人員提供了一種強(qiáng)大的機(jī)制來提供高性能和安全的基礎(chǔ),從而推動(dòng)復(fù)雜的用戶體驗(yàn)??紤]一個(gè)農(nóng)業(yè)儀表板應(yīng)用程序,該應(yīng)用程序需要安全訪問后端 I/O 微控制器或從基于 RTOS 的單板計(jì)算機(jī)提取數(shù)據(jù)的飛機(jī)駕駛艙顯示器。
一種推薦的應(yīng)用程序架構(gòu)是將業(yè)務(wù)邏輯保留在 Rust 后端,并為用戶界面使用 Qt/C++ 插件。這解耦了兩個(gè)域,將 Qt/QML 用于 GUI 開發(fā)的快速迭代速度和靈活性從業(yè)務(wù)級(jí)代碼中分離出來。
如何為嵌入式軟件集成 Rust 和 Qt
將 Qt 與 Rust 集成的常見方法是讓 Rust 調(diào)用 Qt 的 C++ 庫(kù)。雖然有幾種綁定方法,但這些方法通常不是 Rust 慣用的,并且往往會(huì)失去安全優(yōu)勢(shì),如圖 1 所示。此外,Qt 的大多數(shù) Rust 綁定不會(huì)將 Rust 代碼暴露給 C++,這使得它們難以集成到現(xiàn)有的 C++ 代碼庫(kù)中。
一種更有效的方法是以安全的方式連接兩種語言,盡可能多地保留 Rust 固有的安全優(yōu)勢(shì)。為此,Rust 需要能夠使用自己的QObject子類和實(shí)例以的妥協(xié)來擴(kuò)展 Qt 對(duì)象系統(tǒng)。
后一種方法的一個(gè)例子是開源庫(kù)CXX-Qt,它由 KDAB 發(fā)起并管理其正在進(jìn)行的開發(fā)和改進(jìn)。該庫(kù)將 Qt 強(qiáng)大的面向?qū)ο蠛驮獙?duì)象系統(tǒng)與 Rust 相結(jié)合,并基于和擴(kuò)展了另一個(gè)開源 Rust/C++ 互操作性庫(kù)CXX 。在 CXX-Qt 中,新的QObject子類由 Rust 模塊中的項(xiàng)目組成,以執(zhí)行橋接功能。這些子類就像 QML 和 C++ 中的任何其他QObject一樣被實(shí)例化,將 Rust 特性暴露給 Qt。
CXX-Qt 定義的每個(gè)QObject都包含兩個(gè)組件:
一個(gè)基于 C++ 的對(duì)象,它是一個(gè)公開屬性和可調(diào)用方法的包裝器
存儲(chǔ)屬性、實(shí)現(xiàn)可調(diào)用對(duì)象、管理內(nèi)部狀態(tài)并處理來自屬性和后臺(tái)線程的更改請(qǐng)求的Rust結(jié)構(gòu)
CXX-Qt 自動(dòng)生成代碼以在 Rust 和 Qt/C++ 域之間傳輸數(shù)據(jù),并使用名為CXX的庫(kù)在兩者之間進(jìn)行通信。
CXX-Qt 橋接方法的主要原則
為了解釋有效的 Rust-Qt 橋是如何工作的,我們將描述 CXX-Qt 庫(kù)背后的幾個(gè)關(guān)鍵原則。
在 Rust 中聲明 QObject
Qt 的設(shè)計(jì)本質(zhì)上是面向?qū)ο蟮?,?duì)于 C++ 和 QML 都是如此,而 Rust 不支持繼承和多態(tài)性等常見的類特性。為了克服這個(gè)限制,CXX-Qt 在 Rust 中擴(kuò)展了 Qt 對(duì)象系統(tǒng),以提供一種自然的方式來集成兩種語言,同時(shí)保持慣用的 Rust 代碼。
CXX-Qt 橋可以包括以下部分:
模塊周圍的宏指示內(nèi)容與 CXX-Qt 相關(guān)
定義 QObject、Qt 的任何屬性和任何私有狀態(tài)的結(jié)構(gòu)
Qobject 結(jié)構(gòu)的可選實(shí)現(xiàn),其中函數(shù)可以標(biāo)記為 qinvokable,允許從 QML 和 C++ 調(diào)用它們
定義 QObject 信號(hào)的枚舉
普通 CXX 塊
CXX-Qt 在代碼生成期間將此 Rust 模塊擴(kuò)展為QObject的 C++ 子類和RustObj結(jié)構(gòu),如圖 2 所示。
圖 2. CXX-Qt 如何擴(kuò)展 QObjects 以供運(yùn)行時(shí)使用()
下面是一個(gè) QObject 示例,其中包含三個(gè)用于跨域操作的可調(diào)用方法和一個(gè) Rust-only 方法:
#[cxx_qt::bridge]
mod my_object {
不安全的外部“C++”{
包括?。ā癱xx-qt-lib/qstring.h”);
輸入 QString = cxx_qt_lib::QString;
包括?。ā癱xx-qt-lib/qurl.h”);
輸入 QUrl = cxx_qt_lib::QUrl;
}
#[cxx_qt::qobject]
#[推導(dǎo)(默認(rèn))]
酒吧結(jié)構(gòu)我的對(duì)象{
#[q屬性]
is_connected:布爾值,
#[q屬性]
網(wǎng)址:QUrl,
}
#[cxx_qt::qsignals(MyObject)]
酒吧枚舉連接{
連接的,
錯(cuò)誤{消息:QString},
}
實(shí)現(xiàn) qobject::MyObject {
#[qinvokable]
pub fn connect(mut self: Pin<&mut Self>, url: QUrl) {
self.as_mut().set_url(url);
如果自己
.as_ref()
.url()
.to_string()
.starts_with("https://kdab.com")
{
self.as_mut().set_is_connected(true);
self.emit(Connection::Connected);
} 別的 {
self.as_mut().set_is_connected(false);
self.emit(連接::錯(cuò)誤{
message: QString::from("URL 不以 https://kdab.com" 開頭),
});
}
}
}
}
使用#[qinvokable]屬性聲明的方法(例如上面的connect)暴露給 QML 和 C++,參數(shù)和返回類型在 Qt 端匹配。使用#[qproperty] 屬性(例如上面的 is_connected)聲明的 QObject 結(jié)構(gòu)中的字段作為 Q_PROPERTY 公開給 QML 和 C++。用#[cxx_qt::qsignals(T)] 屬性標(biāo)記的枚舉定義了在 QObject T 上聲明的信號(hào)。請(qǐng)注意,CXX-Qt 會(huì)自動(dòng)在 snake_case (Rust) 和 CamelCase (Qt) 命名之間進(jìn)行轉(zhuǎn)換。
在此示例中,connect 方法用于從 Rust 改變 QObject 的 url 和 connected 屬性。然后發(fā)出一個(gè)信號(hào),指示連接是否成功或是否發(fā)生錯(cuò)誤。
沒有用屬性標(biāo)記的方法或字段被認(rèn)為是 Rust 私有的,可用于管理內(nèi)部狀態(tài)或用作線程實(shí)例數(shù)據(jù)。
由于QObject是橋的 C++ 端擁有的構(gòu)造 Qt 對(duì)象,因此它會(huì)在運(yùn)行時(shí)銷毀 C++ 對(duì)象時(shí)被銷毀。
跨橋聲明通用數(shù)據(jù)類型
原始數(shù)據(jù)類型和 CXX 類型可以跨橋使用,不需要在 Rust 和 Qt 之間進(jìn)行轉(zhuǎn)換。庫(kù) cxx_qt_lib 提供代表常見 Qt 類型(例如 QColor、QString、QVariant 等)的 Rust 類型,以供跨橋使用。
隨著項(xiàng)目的發(fā)展,我們計(jì)劃在cxx_qt_lib中加入更多常用的Qt類型,比如Qt容器類型(如QHash和 QVector) 和其他對(duì) Qt C++ 或 QML 有用的類型。然后,我們希望通過向 Rust 生態(tài)系統(tǒng)中已建立的板條箱添加轉(zhuǎn)換來增強(qiáng)這些類型。例如,我們計(jì)劃支持從 QColor 到顏色包類型的轉(zhuǎn)換或從 QDateTime 到日期時(shí)間包類型的轉(zhuǎn)換,或者使用 Rust 包對(duì) Qt 類型進(jìn)行(反)序列化等。
保持線程安全
Rust-Qt 應(yīng)用程序中安全線程背后的一般概念是每當(dāng) Rust 代碼執(zhí)行時(shí)在 C++ 端獲取一個(gè)鎖,以防止該代碼被多個(gè)線程執(zhí)行。這意味著所有直接從 C++ 調(diào)用的 Rust 代碼,例如可調(diào)用對(duì)象,僅在 Qt 線程上執(zhí)行。
為了允許開發(fā)人員將狀態(tài)從后臺(tái) Rust 線??程同步到 Qt,CXX-Qt 提供了一個(gè)幫助程序,允許您從后臺(tái)線程對(duì) Rust 閉包進(jìn)行排隊(duì)。然后從 Qt 事件循環(huán)中執(zhí)行閉包。
// 在可調(diào)用對(duì)象中,請(qǐng)求 qt 線程的句柄
讓 qt_thread = self.qt_thread();
// 產(chǎn)生一個(gè) Rust 線??程
std::thread::spawn(移動(dòng) || {
讓 value = compute_value_on_rust_thread();
// 使用閉包移動(dòng)值并在 Qt 事件循環(huán)中運(yùn)行任務(wù)
線程
.queue(移動(dòng)|mut qobject| {
// 發(fā)生在 Qt 事件循環(huán)中
qobject.set_value(值);
})
.unwrap();
});
將 Rust QObject 暴露給 QML
要在 Qt 應(yīng)用程序中使用 Rust 代碼,必須將 Rust QObject 導(dǎo)出到 QML。CXX-Qt 通過為Rust 中定義的每個(gè)QObject子類生成一個(gè) C++ 類來簡(jiǎn)化這個(gè)過程——開發(fā)人員只需要在 main.cpp 中包含兩個(gè)語句:
包括 QObject 類,例如,
#include "cxx-qt-gen/my_object.h"
將 QObject 類導(dǎo)出到 QML,例如,
qml注冊(cè)類型( "com.kdab.cxx_qt.demo" , 1 , 0 , "MyObject" );
構(gòu)建系統(tǒng)
CXX-Qt 支持使用 CMake 或 Cargo 構(gòu)建。
使用 CMake,Corrosion(以前稱為 cmake-cargo)用于將 Rust crate 作為靜態(tài)庫(kù)導(dǎo)入,然后鏈接到您的 Qt 應(yīng)用程序可執(zhí)行文件或庫(kù)。這允許將 CXX-Qt 輕松集成到現(xiàn)有的 CMake Qt 應(yīng)用程序中。構(gòu)建 Rust crate 時(shí),build.rs 文件會(huì)編譯 CXX-Qt 生成的任何 C++ 代碼。
對(duì)于使用 Rust 的 Cargo 構(gòu)建系統(tǒng)的項(xiàng)目,CXX-Qt 也可以在沒有 CMake 的情況下從 Cargo 使用。使用此方法,Rust 項(xiàng)目的 build.rs 文件還會(huì)觸發(fā) Qt 資源的構(gòu)建、任何其他 C++ 文件的編譯以及鏈接到任何其他 Qt 模塊。
結(jié)論
隨著集成 Rust 和 Qt 的用例的增長(zhǎng),開發(fā)團(tuán)隊(duì)?wèi)?yīng)該知道他們除了一對(duì)一直接綁定之外的選擇。CXX-Qt 庫(kù)提供了一種可行的橋接機(jī)制,以慣用的方式保留了 Rust 的線程安全和性能優(yōu)勢(shì),同時(shí)還允許開發(fā)人員使用普通的 Qt 和 Rust 代碼。
通過理解這里的概念,包括 Qt 對(duì)象和 Rust QObject之間的映射、Rust 的常見 Qt 類型以及定義運(yùn)行時(shí)互操作性的宏和代碼生成,開發(fā)人員可以更好地為他們的應(yīng)用程序開發(fā)選擇正確的 Rust-Qt 集成機(jī)制。
版權(quán)與免責(zé)聲明
凡本網(wǎng)注明“出處:維庫(kù)電子市場(chǎng)網(wǎng)”的所有作品,版權(quán)均屬于維庫(kù)電子市場(chǎng)網(wǎng),轉(zhuǎn)載請(qǐng)必須注明維庫(kù)電子市場(chǎng)網(wǎng),http://udpf.com.cn,違反者本網(wǎng)將追究相關(guān)法律責(zé)任。
本網(wǎng)轉(zhuǎn)載并注明自其它出處的作品,目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點(diǎn)或證實(shí)其內(nèi)容的真實(shí)性,不承擔(dān)此類作品侵權(quán)行為的直接責(zé)任及連帶責(zé)任。其他媒體、網(wǎng)站或個(gè)人從本網(wǎng)轉(zhuǎn)載時(shí),必須保留本網(wǎng)注明的作品出處,并自負(fù)版權(quán)等法律責(zé)任。
如涉及作品內(nèi)容、版權(quán)等問題,請(qǐng)?jiān)谧髌钒l(fā)表之日起一周內(nèi)與本網(wǎng)聯(lián)系,否則視為放棄相關(guān)權(quán)利。
- 深入解析嵌入式 OPENAMP 框架:開啟異核通信新時(shí)代2025/7/22 16:27:29
- 一文快速了解OPENWRT基礎(chǔ)知識(shí)2025/7/14 16:59:04
- 獨(dú)立 ADC 優(yōu)勢(shì)大揭秘:為何不可替代?2025/7/7 16:21:04
- 深入剖析:嵌入式中 RS485、RS422 和 RS232 的特點(diǎn)差異2025/7/5 15:07:54
- 揭秘嵌入式 MCU:浮點(diǎn)數(shù)據(jù)處理難點(diǎn)及應(yīng)對(duì)策略2025/6/20 15:19:07
- 交交型變頻器和交直交型變頻器的區(qū)別
- 800G DR8 與其他 800G 光模塊的對(duì)比分析
- UPS電源中電流電壓傳感器的應(yīng)用
- 51單片機(jī)電平特性是什么
- AC/DC 與 DC/DC 轉(zhuǎn)換器的工作原理與應(yīng)用
- PCB板層設(shè)計(jì)與電磁兼容性的關(guān)系解析
- 接觸器式繼電器怎么區(qū)分 如何測(cè)量好壞
- 車規(guī)級(jí)MCU介紹及應(yīng)用場(chǎng)景
- 電容選型時(shí)如何選擇產(chǎn)品的電壓
- 線性穩(wěn)壓電源與開關(guān)穩(wěn)壓電源:原理、特性及應(yīng)用