2009-09-10 3 views
22

Этот вопрос уже задан на этом форуме, но я не понимаю эту концепцию.Как сигналы и слоты реализованы под капотом?

Я читал вокруг, и кажется, что сигнал и слоты реализованы с использованием указателей функций. Сигнал является одной большой функцией, которая внутри него вызывает все подключенные слоты (указатели функций). Это верно? И какова роль сгенерированных файлов moc во всей истории? Я не понимаю, как функция сигнала знает, какие слоты для вызова, какие слоты подключены к этому сигналу.

Спасибо за ваше время

+0

Хороший вопрос. См. Также: http://stackoverflow.com/questions/1413777/how-boost-implements-signals-and-slots – elcuco

ответ

15

Qt реализует эти вещи таким образом, что напоминает интерпретируемые языки. То есть он строит таблицы символов, которые отображают имена сигналов в указатели на функции, поддерживает их и ищет нужный указатель функции по имени функции.

Каждый раз, когда вы излучать сигнал, т.е. написать

emit something(); 

вы на самом деле вызвать функцию something(), что он автоматически генерируемый мета объекта компилятором и помещают в *.moc файл. В рамках этой функции проверяется, к каким слотам подключен этот сигнал в настоящий момент, и соответствующие функции слотов (которые вы реализовали в ваших собственных источниках) последовательно вызываются через таблицы символов (описанным выше способом). И emit, как и другие ключевые слова Qt, просто отбрасываются препроцессором C++ после создания *.moc. Действительно, в одном из заголовков Qt (qobjectdefs.h), существуют такие строки:

#define slots 
#define signals protected 
#define emit 

функция соединения (connect) просто изменяет таблицы символов поддерживается в пределах *.moc файлов, а также аргументы, переданные ему (с SIGNAL() и ` SLOT) также предварительно обрабатываются для соответствия таблицам.

Это общая идея. В своем другом ответе ジョージ поставляет нам ссылки to trolltech mailing list и another SO question на эту тему.

+4

В принципе, вы можете установить контрольную точку на выходе, а затем пройти через процесс сигнализации. В то время как обычно доставляется напрямую, сигналы могут быть поставлены в очередь, это необходимо, если вы хотите подключить два объекта в разных потоках –

5

Думаю, я должен добавить следующее.

Существует another linked question - и есть a very good article, что можно считать довольно подробным расширением до его answer; here is this article again, с улучшенной (хотя и не идеальной) подсветкой синтаксиса кода.

Вот мой краткий пересказ его, которые могут быть склонны к ошибкам) ​​

В основном, когда мы вставляем Q_OBJECT макрос в нашем определении класса, препроцессор расширяет его к статическому декларации QMetaObject например, тот, который будет совместно всеми экземплярами одного и того же класса:

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

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

} 

Этот экземпляр, в свою очередь, при инициализации будет хранить подписи ("methodname(argtype1,argtype2)") сигналов и слотов, то, что позволит реализоватьвызова, который возвращает, а индекс метода по его подписи строка:

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; 
}; 

Теперь, когда moc создает файл moc_headername.cpp для класса заголовка Qt headername.h, он ставит там подписные строки и другие данные, которые необходимы для правильная инициализация структуры d, а затем с помощью этих данных записывается код инициализации для staticMetaObject singleton.

Еще одна важная вещь, которую она делает, это поколение кода qt_metacall() метода объекта, который принимает метод идентификатор объекта и массив аргументов указателей и вызывает метод через длинный switch как это:

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 ... 
} 

Наконец, для каждого сигнал moc генерирует реализацию, которая содержит 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 
         ); 
} 

Наконец, connect() вызова переводит StrI ng к их целым идентификаторам (те, которые используются qt_metacall()) и поддерживает список соединений «сигнал-слот»; когда сигнал испускается, код activate() проходит через этот список и вызывает соответствующие «слоты» объекта через их метод qt_metacall().

Подводя итог, статический экземпляр QMetaObject хранит «метаинформацию» (строки подписи метода и т. Д.), Сгенерированный метод qt_metacall() предоставляет «таблицу методов», которая позволяет вызывать любой сигнал/слот индексом, сигналом реализации, генерируемые moc, используют эти индексы через activate(), и, наконец, connect() выполняет работу по поддержанию списка карт индексов «сигнал-слот».

* Примечание: существует сложность этой схемы, используемой для случая, когда мы хотим доставлять сигналы между различными потоками (я подозреваю, что нужно смотреть на код blocking_activate()), но я надеюсь, что общая идея остается той же)

Это мой очень грубое понимание связанного изделия, которое легко может быть неправильно, поэтому я рекомендую пойти и прочитать его прямо)

PS. Поскольку я хотел бы улучшить свое понимание реализации Qt - сообщите мне о любых несоответствиях в моем пересказе!


С другим моим (ранее) ответа был удален каким-то рьяным редактором, я добавить текст здесь (я пропускаю несколько деталей, которые были не включены в посте Павла Shved, и я сомневаюсь, что человек, который удалил ответ.)

@Pavel Швед:

Я уверен, что где-то в Qt заголовков существует строка:

#define emit

Просто, чтобы подтвердить: нашел в старом коде Qt путем поиска по коду Google. Вполне вероятно, что он все еще существует); найденный маршрут местоположения был:

ftp://ftp.slackware-brasil.com.br> Slackware-7.1> вно> КДЭ-1,90> кварты-2.1.1.tgz> USR> Lib> Qt-2.1.1> подачи> ядро> qobjectdefs.h


Другой complementory ссылка: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html - смотрите ответ на Andreas Pakulat


А вот другая часть ответа: Qt question: How do signals and slots work?