2011-01-11 40 views
28

Tôi đã thực hiện rất nhiều nghiên cứu về xử lý lỗi với Qt/C++ và tôi vẫn bị mất khi tôi bắt đầu. Có lẽ tôi đang tìm kiếm một cách dễ dàng (như các ngôn ngữ khác cung cấp). Một, đặc biệt, cung cấp cho một ngoại lệ unhandled mà tôi sử dụng tôn giáo. Khi chương trình gặp sự cố, chương trình sẽ ném ngoại lệ không được xử lý để tôi có thể tạo báo cáo lỗi của riêng mình. Báo cáo đó được gửi từ máy khách hàng của tôi đến máy chủ trực tuyến mà sau đó tôi đọc sau.Qt/C++ Xử lý lỗi

Vấn đề mà tôi gặp phải với C++ là bất kỳ lỗi xử lý nào được thực hiện đều phải được suy nghĩ về TRƯỚC KHI tay (nghĩ thử/nắm bắt hoặc điều kiện lớn). Theo kinh nghiệm của tôi, các vấn đề trong mã không được nghĩ đến trước khi tay khác không có vấn đề gì để bắt đầu.

Viết ứng dụng đa nền tảng mà không có cơ chế xử lý lỗi/báo cáo/theo dõi đa nền tảng là một chút đáng sợ đối với tôi.

Câu hỏi của tôi là: Có loại cơ chế bẫy lỗi “bắt tất cả” Qt hoặc C++ cụ thể mà tôi có thể sử dụng trong ứng dụng của mình để, nếu có điều gì sai, ít nhất tôi có thể viết báo cáo trước Nó bị treo?

Ví dụ:


class MainWindow: public QMainWindow 
{ 
[...] 

public slots: 
void add_clicked(); 
} 

void MainWindow::add_clicked() 
{ 
    QFileDialog dlg(this, Qt::Sheet); 
    QString filename = dlg.getOpenFileName(this); 

    if(!filename.isEmpty()) 
    { 
     QStringList path = filename.split(QDir::separator()); 
     QString file = path.at(path.count()); // Index out of range assertion. 

     if(!lst_tables->openDatabase(filename)) 
     { 
      [...] 
     } 
    } 
} 

Tôi muốn lỗi này bị bắt là một ngoại lệ unhandled VÀ ứng dụng để bỏ mà không hiển thị cho người dùng cửa sổ tai nạn mặc định trên hệ điều hành Windows/Mac. Tôi chỉ muốn nó thoát độc đáo sau khi viết thông báo xác nhận vào một tệp, v.v.

Trả lời

28

Ghi đè QCoreApplication::notify() và thêm try-catch vào đó. Điều đó, và một cái gì đó trong chính() bao gồm hầu hết các trường hợp trong kinh nghiệm của tôi.

Đây là loại cách tôi thực hiện. Lưu ý rằng tôi đang sử dụng C++ RTTI ở đây, không phải phiên bản Qt, nhưng đó chỉ là để thuận tiện trong các ứng dụng của chúng tôi. Ngoài ra, chúng tôi đưa ra một QMessageBox với các thông tin và một liên kết đến tập tin đăng nhập của chúng tôi. Bạn nên mở rộng theo nhu cầu của riêng bạn.

bool QMyApplication::notify(QObject* receiver, QEvent* even) 
{ 
    try { 
     return QApplication::notify(receiver, event); 
    } catch (std::exception &e) { 
     qFatal("Error %s sending event %s to object %s (%s)", 
      e.what(), typeid(*event).name(), qPrintable(receiver->objectName()), 
      typeid(*receiver).name()); 
    } catch (...) { 
     qFatal("Error <unknown> sending event %s to object %s (%s)", 
      typeid(*event).name(), qPrintable(receiver->objectName()), 
      typeid(*receiver).name()); 
    }   

    // qFatal aborts, so this isn't really necessary 
    // but you might continue if you use a different logging lib 
    return false; 
} 

Ngoài ra, chúng tôi sử dụng __try, __except trên Windows để bắt ngoại lệ không đồng bộ (vi phạm truy cập). Google Breakpad có thể phục vụ như là một thay thế đa nền tảng cho điều đó.

+0

Bạn có thể chỉ cho tôi một ví dụ về thử trò chơi của bạn ở đó không? –

+0

@ShiGon: Xong ... – Macke

+0

Tôi nghĩ mình đang làm điều gì đó sai vì tôi không nhận được tình yêu từ bất kỳ thử thách nào trừ khi tôi sử dụng lệnh ném bản thân mình. Vì vậy, khó hiểu. Và thậm chí sau đó, ứng dụng vẫn gặp sự cố và cố gửi báo cáo lỗi cho Apple trong trường hợp này. Tôi đang cố gắng để ứng dụng từ bỏ một cách duyên dáng. –

10

Bạn có thể đặt một catch (...) trong hoặc xung quanh main() Dưới đây là xung quanh:

int main() try 
{ 
    ... 
} 
catch (std::exception & e) 
{ 
    // do something with what... 
} 
catch (...) 
{ 
    // someone threw something undecypherable 
} 
+0

Ngoài ra, thêm các kiểu xử lý ngoại lệ kiểu c bắt vi phạm truy cập, ngăn xếp ngăn xếp và một vài ngoại lệ hữu ích khác. – Macke

+1

@Marcus bạn chỉ có thể bắt vi phạm truy cập và ngăn xếp tràn qua xử lý ngoại lệ trên Windows bằng cách sử dụng MSVC, theo như tôi biết ... nó không thể di chuyển được. – rohanpm

+1

@Jerkface: True. Tôi không biết tại sao tôi lại giả sử Windows trong trường hợp này. : - | – Macke

5

Google Breakpad là một khuôn khổ báo cáo lỗi ứng dụng đa nền tảng. Có lẽ nó giúp?

(Tôi đã không thử nó trong c của chúng tôi ++/apps qt, nhưng tôi rất muốn có được xung quanh để nó một ngày nào đó ...)

2

Qt không thường sử dụng, hoặc hoàn toàn hỗ trợ ngoại lệ ném (nếu bạn có thể belive đó!)

Kiểm tra những liên kết này:

Why doesn't Qt use exception handling?

http://doc.qt.io/qt-5/exceptionsafety.html

Điều đó nói rằng, câu trả lời từ @Crazy Eddie và @Macke là khá tốt, nhưng không phải lúc nào cũng hiệu quả. Đặc biệt, tôi thấy rằng bạn không thể sử dụng một trong số chúng từ một hàm vùng mà bạn đã gọi từ QML. Vì vậy, tôi tạo ra một công việc hacky xung quanh cho vấn đề này. * Sử dụng điều này kết hợp với của họ - không phải ở vị trí của nó.

Trước tiên, tôi đã tạo lớp bắt nguồn từ QException, tôi sẽ bỏ qua đây, nhưng có thể bạn sẽ muốn làm điều gì đó. Trong bài viết này, tôi chỉ gọi nó là "MyQException".

Dù sao, thêm tiêu đề này cho một lớp được gọi là QmlSlotThrower:

#ifndef QMLSLOTTHROWER_H 
#define QMLSLOTTHROWER_H 

#include "MyQException.h" 

class QmlSlotThrower 
{ 
public: 
    static QmlSlotThrower *get() 
    { 
     static QmlSlotThrower instance; 
     return &instance; 
    } 
    QmlSlotThrower(QmlSlotThrower const&) = delete; 
    void operator=(QmlSlotThrower const&) = delete; 

    void throwToTop(const MyQException &exception); 

private: 
    QmlSlotThrower(){} 
}; 
static QmlSlotThrower *qmlSlotThrower = QmlSlotThrower::get(); 

#define throwFromQmlSlot(exc) qmlSlotThrower->throwToTop(exc); return; 

#endif // QMLSLOTTHROWER_H 

Sau đó, nó cpp:

#include "QmlSlotThrower.h" 
#include <QTimer> 

class AsynchronousThrower: public QObject 
{ 
Q_OBJECT 
public: 
    void throwThis(const MyQException &exception) 
    { 
     exception_ = exception; 
     QTimer::singleShot(0, this, SLOT(throwIt())); 
    } 
private slots: 
    void throwIt(){ throw exception_; } 
private: 
    MyQException exception_; 
}; 
static AsynchronousThrower asycnThrower; 

// This is needed to allow the Q_OBJECT macro 
// to work in the private classes 
#include "QmlSlotThrower.moc" 

// -------------------------------- 

void QmlSlotThrower::throwToTop(const MyQException &exception) 
{ asycnThrower.throwThis(exception); } 

Cuối cùng, đây là một thực hiện ví dụ:

void someQMLSlot() 
{ 
    // Qt has been progressively adding exception handling 
    // support, but you still cannot throw from a QML 
    // triggered slot. It causes an uncatchable fatal error! 

    // As a general rule, don't throw in Qt unless you are 
    // certain something is there to catch it. You cannot 
    // count on an uncaught exception handler at a top level 
    // to always work. This QML problem is a perfect example. 

    // So this is not an option here! 
    //throw MyQException("Something terrible occured!"); 

    // This work around, however, can be used instead! 
    //throwFromQmlSlot(MyQException("Something terrible occured!")) 

    // Or, to be more robust in illustrating how you can still use 
    // normal throws from nested functions even, you can do this: 
    try{ throw MyQException("Something terrible occured!"); } 
    catch(const MyQException &e) { throwFromQmlSlot(e) } 

    qDebug() << "YOU SHOULD NEVER SEE THIS!!"; 
} 

SỬ DỤNG CHỈ THE MACRO TRỰC TIẾP TỪ SLOT CỦA BẠN!