2009-09-10 13 views
22

Câu hỏi này đã được hỏi trong diễn đàn này nhưng tôi không hiểu khái niệm này.Tín hiệu và khe được thực hiện dưới mui xe như thế nào?

Tôi đã đọc xung quanh và có vẻ như tín hiệu và khe được thực hiện bằng cách sử dụng con trỏ chức năng tức là tín hiệu là một hàm lớn bên trong nó gọi tất cả các khe được kết nối (con trỏ hàm). Điều này có đúng không? Và vai trò của các tập tin được tạo ra trong toàn bộ câu chuyện là gì? Tôi không hiểu làm thế nào chức năng tín hiệu biết được khe nào để gọi i.e khe nào được kết nối với tín hiệu này.

Cảm ơn thời gian của bạn

+0

đẹp câu hỏi. Xem thêm: http://stackoverflow.com/questions/1413777/how-boost-implements-signals-and-slots – elcuco

Trả lời

15

Qt thực hiện những điều này giống như ngôn ngữ thông dịch. I E. nó xây dựng các bảng biểu tượng ánh xạ các tên tín hiệu cho các con trỏ hàm, duy trì chúng và tìm kiếm con trỏ hàm bằng tên hàm khi cần.

Mỗi khi bạn phát ra một tín hiệu, ví dụ: viết

emit something(); 

bạn thực sự gọi something() chức năng, mà nó tự động được tạo ra bởi trình biên dịch đối tượng meta và đặt vào một tập tin *.moc. Trong hàm này, nó đã kiểm tra những khe mà tín hiệu này được kết nối vào lúc này và các chức năng khe thích hợp (mà bạn đã thực hiện trong các nguồn của riêng bạn) được gọi tuần tự thông qua các bảng biểu tượng (theo cách được mô tả ở trên). Và emit, giống như các từ khóa Qt cụ thể khác, chỉ bị loại bỏ bởi bộ tiền xử lý C++ sau khi *.moc được tạo. Thật vậy, trong một trong những tiêu đề Qt (qobjectdefs.h), có tồn tại dòng như:

#define slots 
#define signals protected 
#define emit 

chức năng kết nối (connect) chỉ làm thay đổi các bảng biểu tượng duy trì trong vòng *.moc tập tin, và các đối số được truyền cho nó (với SIGNAL() và ' Các macro SLOT cũng được xử lý trước để khớp với các bảng.

Đó là ý tưởng chung. Trong câu trả lời khác của mình, ジョージ cung cấp cho chúng tôi các liên kết to trolltech mailing list và đến another SO question về chủ đề này.

+4

Về cơ bản là chính xác, bạn có thể đặt điểm ngắt tại vị trí phát ra và sau đó bước qua quá trình báo hiệu. Trong khi thường được phân phối trực tiếp, tín hiệu mayb được xếp hàng đợi, điều này là cần thiết khi bạn muốn kết nối hai đối tượng trong các chủ đề khác nhau –

5

Tôi nghĩ tôi nên thêm phần sau đây.

another linked question - và có a very good article có thể được xem là mở rộng khá chi tiết cho số answer; here is this article again, với cú pháp mã được cải thiện (mặc dù vẫn chưa hoàn hảo).

Dưới đây là kể lại của tôi ngắn của nó, mà bạn có thể dễ bị sai lầm)

Về cơ bản khi chúng ta chèn Q_OBJECT vĩ mô trong định nghĩa lớp của chúng tôi, tiền xử lý mở rộng nó vào một tuyên bố QMetaObject dụ tĩnh, một trong đó sẽ được chia sẻ bởi tất cả các trường của cùng một lớp:

class ClassName : public QObject // our class definition 
{ 
    static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this 

    // ... signal and slots definitions, other stuff ... 

} 

dụ này, đến lượt nó, về khởi tạo sẽ lưu trữ các chữ ký ("methodname(argtype1,argtype2)") của các tín hiệu và các khe, những gì sẽ cho phép để thực hiện các indexOfMethod() cuộc gọi, mà trả về, tốt, chỉ số phương pháp bằng cách đó là chuỗi chữ ký:

struct Q_CORE_EXPORT QMetaObject 
{  
    // ... skip ... 
    int indexOfMethod(const char *method) const; 
    // ... skip ... 
    static void activate(QObject *sender, int signal_index, void **argv); 
    // ... skip ... 
    struct { // private data 
     const QMetaObject *superdata; // links to the parent class, I guess 
     const char *stringdata; // basically, "string1\0string2\0..." that contains signatures and other names 
     const uint *data; // the indices for the strings in stringdata and other stuff (e.g. flags) 
     // skip 
    } d; 
}; 

Bây giờ khi moc tạo ra các tập tin moc_headername.cpp cho lớp tiêu đề Qt headername.h, nó đặt có các chuỗi chữ ký và các dữ liệu khác đó là cần thiết cho khởi tạo chính xác cấu trúc d và sau đó viết mã khởi tạo cho singleton staticMetaObject bằng cách sử dụng dữ liệu này.

Một điều quan trọng nó là thế hệ của mã cho phương pháp qt_metacall() của đối tượng, mà sẽ đưa id phương pháp của một đối tượng và một mảng của con trỏ đối số và gọi phương pháp này qua một chặng đường dài switch như thế này:

int ClassName::qt_metacall(..., int _id, void **_args) 
{ 
    // ... skip ... 
    switch (_id) { 
     case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args 
     case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument 
     // ... etc ... 
    } 
    // ... skip ... 
} 

cuối cùng, đối với mỗi tín hiệu moc tạo ra một thực hiện, trong đó có một cuộc gọi QMetaObject::activate():

void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */) 
{ 
    void *_args[] = { 0, // this entry stands for the return value 
         &arg1, // actually, there's a (void*) type conversion 
         &arg2, // in the C++ style 
         // ... 
        }; 
    QMetaObject::activate(this, 
          &staticMetaObject, 
          0, /* this is the signal index in the qt_metacall() map, I suppose */ 
          _args 
         ); 
} 

cuối cùng, cuộc gọi connect() dịch các STRI ng chữ ký phương thức để id số nguyên của họ (những cái được sử dụng bởi qt_metacall()) và duy trì một danh sách các kết nối tín hiệu để khe; khi tín hiệu được phát ra, mã activate() đi qua danh sách này và gọi đối tượng thích hợp là "khe" qua phương thức qt_metacall() của chúng.

Để tổng hợp, trường hợp QMetaObject tĩnh lưu trữ "siêu thông tin" (phương thức chữ ký chuỗi vv), phương thức qt_metacall() được tạo ra cung cấp "bảng phương thức" cho phép bất kỳ tín hiệu/khe nào được gọi bằng chỉ mục, tín hiệu triển khai được tạo ra bởi moc sử dụng các chỉ mục này qua activate() và cuối cùng là connect() thực hiện công việc duy trì danh sách các bản đồ chỉ mục tín hiệu đến vị trí.

* Lưu ý: có một biến chứng của lược đồ này được sử dụng cho trường hợp chúng tôi muốn gửi tín hiệu giữa các luồng khác nhau (tôi nghi ngờ rằng phải xem mã blocking_activate()), nhưng tôi hy vọng ý tưởng chung vẫn giữ nguyên)

Đây là sự hiểu biết rất thô của tôi về bài viết liên quan, trong đó một cách dễ dàng có thể sai, vì vậy tôi khuyên bạn nên đi và đọc nó trực tiếp)

PS. Khi tôi muốn cải thiện sự hiểu biết của mình về việc triển khai Qt - hãy cho tôi biết về bất kỳ sự mâu thuẫn nào trong việc kể lại của tôi!


Kể từ khác (trước đó) câu trả lời của tôi đã bị xóa bởi một số biên tập viên sốt sắng, tôi sẽ nối các văn bản ở đây (tôi đang thiếu vài chi tiết mà là không đưa vào bài Pavel Shved, và tôi nghi ngờ người đó người đã xóa câu trả lời được chăm sóc.)

@Pavel Shved:

Tôi khá chắc chắn rằng đâu đó trong tiêu đề Qt tồn tại một dòng:

#define emit

Chỉ để xác nhận: tìm thấy nó trong mã Qt cũ Tìm kiếm bằng Google Code. Rất có khả năng nó vẫn còn đó); con đường vị trí tìm thấy là:

ftp://ftp.slackware-brasil.com.br> slackware-7.1> contrib> kde-1.90> qt-2.1.1.tgz> usr> lib> qt-2.1.1> src> kernel> qobjectdefs.h


Một liên kết complementory: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html - xem câu trả lời bởi Andreas Pakulat


Và đây là một phần của câu trả lời: Qt question: How do signals and slots work?