C++判斷QMetaObject::invokeMethod()里的函數(shù)是否調(diào)用成功
今天,在Qt編程,碰到一個(gè)需要使用invokeMethod方式來獲取函數(shù)是否執(zhí)行成功的情況。
invokeMethod()即可以同步調(diào)用,也可以異步調(diào)用。若調(diào)用者、被調(diào)用者,都在同一個(gè)線程,則是同步調(diào)用;若調(diào)用者、被調(diào)用者,在不同線程,則是異步調(diào)用。
注意:只有同步調(diào)用,才能通過invokeMethod()的返回值,來判斷函數(shù)是否執(zhí)行成功。
比如,有如下精簡代碼:
//1)業(yè)務(wù)代碼
class ComWork : public QObject {
Q_OBJECT
public:
ComWork();
signals:
void sigSendResult(bool bOK);
public slots:
void SendCommand(QByteArray by) {
qint64 nRet = m_pSerial->write(by);
bool bOK = (nRet != -1);
if(bOK)
m_pSerial->flush();
emit sigSendResult(bOK);
}
private:
QSerialPort* m_pSerial;
};
//2) 界面邏輯代碼
class ZoomWidget : public QWidget {
public:
ZoomWidget (QWidget *parent= NULL);
protected slots:
void OnRecvCmdResult(bool bOK);
private:
QThread* m_pThread; //子線程
ComWork* m_pWork;
bool m_bResult;
};
ZoomWidget ::ZoomWidget (QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
m_pThread = new QThread(this);
m_pWork = new ComWork();
m_pWork->moveToThread(m_pThread); //m_pWork移動(dòng)到子線程里,語句(a)
//連接信號(hào),語句(b)
connect(m_pWork,&m_pWork::sigSendResult,this,ZoomWidget::OnRecvCmdResult);
}
ZoomWidget::OnRecvCmdResult(bool bOK)
{
m_bResult = bOK; //語句(c)
qDebug()<<"recv status:"<<bOK;
}
// 3)核心調(diào)用
ZoomWidget::DoSend()
{
//進(jìn)行調(diào)用,語句(d)
QMetaObject::invokeMethod(m_pWork, "SendCommand", Qt::QueuedConnection, Q_ARG(QByteArray, sendData));
}
在Qt中,UI對(duì)象必須在主線程。而ZoomWidget是一個(gè)QWidtget,屬于UI對(duì)象,即ZoomWidget在主線程。
由語句(a)可知,m_pWork移動(dòng)到了子線程里,即ZoomWidget與m_pWork不在同一個(gè)線程,
則在調(diào)用DoSend()函數(shù)時(shí),QMetaObject::invokeMethod(m_pWork,…)其實(shí)是跨線程調(diào)用,也就是異步調(diào)用。而異步調(diào)用,由于"它不會(huì)等待函數(shù)是否執(zhí)行,就直接返回了",即invokeMethod(,…Qt::QueuedConnection ,)函數(shù),不會(huì)等待m_pWork的SendCommand()是否執(zhí)行,就直接返回,并給出一個(gè)返回值,該返回值是無效的。
如果強(qiáng)制返回,Qt編譯也會(huì)報(bào)"QMetaMethod::invoke: Unable to invoke methods with return values in queued connections"錯(cuò)誤。
但可以通過信號(hào)槽的方式,把異步調(diào)用的結(jié)果(比如語句(b)、語句©),返回給調(diào)用者(比如本例的ZoomWidget)。
這個(gè) "Unable to invoke methods"的Qt 編譯報(bào)錯(cuò):
QMetaMethod::invoke: Unable to invoke methods with return values in queued connections
是一個(gè)非常常見的運(yùn)行時(shí)錯(cuò)誤,它的含義是:
你試圖使用 Qt::QueuedConnection(即異步方式)調(diào)用一個(gè)帶有返回值的方法,而這是 Qt 不支持的。
錯(cuò)誤原因詳解
什么是Qt::QueuedConnection?
- 它表示方法調(diào)用會(huì)在目標(biāo)對(duì)象所在的線程中排隊(duì)執(zhí)行(異步調(diào)用),而不是立即執(zhí)行。
- 常用于跨線程通信,比如從子線程調(diào)用主線程的 UI 方法。
為什么不能在Qt::QueuedConnection中使用帶返回值的函數(shù)?
- 因?yàn)樗?strong>異步調(diào)用,調(diào)用者不會(huì)等待函數(shù)執(zhí)行完成。
- 所以無法通過
Q_RETURN_ARG(...)拿到函數(shù)的返回值。 - Qt 在底層會(huì)檢測(cè)這一點(diǎn),并拋出警告或?qū)е路祷刂禑o效。
示例代碼:錯(cuò)誤寫法
int result = 0;
bool success = QMetaObject::invokeMethod(obj, "addNumbers",
Qt::QueuedConnection, // ? 異步調(diào)用
Q_RETURN_ARG(int, result),
Q_ARG(int, 3),
Q_ARG(int, 5));
上面這段代碼會(huì)導(dǎo)致報(bào)錯(cuò):
QMetaMethod::invoke: Unable to invoke methods with return values in queued connections
正確做法
? 方法一:使用Qt::DirectConnection(同步調(diào)用)
如果你需要獲取返回值,請(qǐng)使用同步連接方式:
int result = 0;
bool success = QMetaObject::invokeMethod(obj, "addNumbers",
Qt::DirectConnection, // ? 同步調(diào)用
Q_RETURN_ARG(int, result),
Q_ARG(int, 3),
Q_ARG(int, 5));
if (success) {
qDebug() << "Result:" << result;
}
?? 注意:Qt::DirectConnection 要求調(diào)用線程和目標(biāo)對(duì)象處于同一個(gè)線程,否則行為未定義。
? 方法二:避免返回值 + 使用信號(hào)傳遞結(jié)果(適合異步場景)
如果你確實(shí)需要跨線程調(diào)用并想獲取結(jié)果,可以這樣做:
步驟如下:
- 將原函數(shù)改為無返回值;
- 使用 QMetaObject::invokeMethod() 調(diào)用它;
- 函數(shù)內(nèi)部處理完后,發(fā)出一個(gè)信號(hào)把結(jié)果傳回來。
示例代碼:
class Worker : public QObject {
Q_OBJECT
signals:
void resultReady(int result); // 用于返回結(jié)果
public slots:
void addNumbersAsync(int a, int b) {
int result = a + b;
emit resultReady(result); // 發(fā)送結(jié)果
}
};
調(diào)用方式:
Worker* worker = new Worker();
worker->moveToThread(thread);
// 連接信號(hào)與槽來接收結(jié)果
connect(worker, &Worker::resultReady, this, [](int res) {
qDebug() << "異步返回結(jié)果:" << res;
});
// 異步調(diào)用
QMetaObject::invokeMethod(worker, "addNumbersAsync",
Qt::QueuedConnection,
Q_ARG(int, 3),
Q_ARG(int, 5));
總結(jié)1
| 場景 | 是否允許返回值 | 推薦連接方式 | 備注 |
|---|---|---|---|
| 同一線程調(diào)用,需返回值 | ? 是 | Qt::DirectConnection | 可使用 Q_RETURN_ARG |
| 跨線程調(diào)用,需返回值 | ? 否 | ? 不可用 | 必須用信號(hào)傳遞結(jié)果 |
| 跨線程調(diào)用,不需要返回值 | ? 是 | Qt::QueuedConnection | 正常使用 |
QMetaObject::invokeMethod()的返回值類型
bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, ...)
返回值說明:
- 返回類型:bool
- 返回值含義:
- true:表示方法成功調(diào)用。
- false:表示方法調(diào)用失敗,可能原因包括:
- 沒有找到名字匹配的方法(函數(shù)名錯(cuò)誤或未聲明為 Q_INVOKABLE 或 slot)。
- 參數(shù)類型不匹配。
- 對(duì)象已經(jīng)被刪除(懸空指針)。
- 使用了 Qt::QueuedConnection 但目標(biāo)對(duì)象沒有運(yùn)行事件循環(huán)。
如何進(jìn)一步排查失敗原因?
雖然返回值只能告訴你是否成功,但你可以通過以下方式定位問題:
1. 檢查函數(shù)是否被正確聲明為Q_INVOKABLE或slot
class Worker : public QObject {
Q_OBJECT
public slots:
void OpenPort(const QString &portName, int baudRate); // 必須匹配參數(shù)類型
};
或者:
Q_INVOKABLE void OpenPort(const QString &portName, int baudRate);
2. 確保對(duì)象沒有被釋放(避免懸空指針)
確保 m_pWork 是一個(gè)有效的 QObject* 指針,且對(duì)象尚未被 delete。
3. 參數(shù)類型必須一致(支持元對(duì)象系統(tǒng))
確保你使用的參數(shù)類型是 Qt 元對(duì)象系統(tǒng)支持的類型(如 int, QString, double 等),或者自定義類型已注冊(cè):
Q_DECLARE_METATYPE(MyCustomType) qRegisterMetaType<MyCustomType>();
4. 調(diào)試輸出所有可用方法(用于排查函數(shù)名/參數(shù)是否正確)
const QMetaObject* metaObj = m_pWork->metaObject();
for (int i = 0; i < metaObj->methodCount(); ++i) {
qDebug() << metaObj->method(i).signature();
}
總結(jié)2
| 內(nèi)容 | 說明 |
|---|---|
| invokeMethod() 是否有返回值? | ? 有,返回 bool 類型 |
| true 表示什么? | 方法調(diào)用成功 |
| false 表示什么? | 方法調(diào)用失?。ê瘮?shù)名錯(cuò)誤、參數(shù)不匹配、對(duì)象無效、異步調(diào)用等) |
| 可以用來做什么? | 判斷函數(shù)是否被成功調(diào)用,用于調(diào)試和錯(cuò)誤處理 |
到此這篇關(guān)于判斷QMetaObject::invokeMethod()里的函數(shù)是否調(diào)用成功的文章就介紹到這了,更多相關(guān)QMetaObject::invokeMethod()調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中的多態(tài)問題—理解虛函數(shù)表及多態(tài)實(shí)現(xiàn)原理
這篇文章主要介紹了C++中的多態(tài)問題—理解虛函數(shù)表及多態(tài)實(shí)現(xiàn)原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
Opencv Hough算法實(shí)現(xiàn)圖片中直線檢測(cè)
這篇文章主要為大家詳細(xì)介紹了Opencv Hough算法實(shí)現(xiàn)圖片中直線檢測(cè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
C++實(shí)現(xiàn)一個(gè)簡易版的事件(Event)的示例代碼
之前在?windows系統(tǒng)中開發(fā)應(yīng)用時(shí),?遇到需要進(jìn)行線程同步的時(shí)候幾乎都是使用的事件內(nèi)核對(duì)象?Event。本文為大家整理了C++實(shí)現(xiàn)一個(gè)簡易版的事件(Event)的相關(guān)資料,需要的可以參考一下2022-11-11
Qt圖形圖像開發(fā)之曲線圖表模塊QChart庫讀取/設(shè)置X軸的顯示區(qū)間
這篇文章主要介紹了Qt圖形圖像開發(fā)之曲線圖表模塊QChart庫讀取/設(shè)置X軸的顯示區(qū)間,需要的朋友可以參考下2020-03-03
C++中的不規(guī)則二維數(shù)組實(shí)現(xiàn)代碼
本文介紹了一個(gè)在C++中保存不定長二維數(shù)組的數(shù)據(jù)結(jié)構(gòu),在這個(gè)結(jié)構(gòu)中,我們使用了一個(gè)含有指針和數(shù)組長度的結(jié)構(gòu)體,用這樣的一個(gè)結(jié)構(gòu)體構(gòu)造一個(gè)結(jié)構(gòu)體數(shù)組,用于存儲(chǔ)每一個(gè)不定長的數(shù)組,感興趣的朋友一起看看吧2024-03-03
C語言中數(shù)據(jù)如何存儲(chǔ)進(jìn)內(nèi)存揭秘
使用編程語言進(jìn)行編程時(shí),需要用到各種變量來存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么2022-08-08
非常漂亮的新年祝福!C語言實(shí)現(xiàn)漂亮的煙花效果
非常漂亮的新年祝福!這篇文章主要介紹了C語言實(shí)現(xiàn)漂亮的煙花效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
C語言中棧的結(jié)構(gòu)和函數(shù)接口的使用示例
這篇文章主要介紹了C語言中棧的結(jié)構(gòu)和函數(shù)接口的使用,類似很多軟件都有撤銷的操作,這其實(shí)就是用棧這種方法來實(shí)現(xiàn)的,當(dāng)然不同的軟件具體實(shí)現(xiàn)代碼會(huì)有差異,不過原理大多都是一樣的2023-02-02
關(guān)于統(tǒng)計(jì)數(shù)字問題的算法
本文介紹了統(tǒng)計(jì)數(shù)字問題的算法,計(jì)算出書的全部頁碼中分別用到多少次數(shù)字0,1,2,3,.....9,并有每一步的解題思路,需要的朋友可以參考下2015-08-08

