Использование emit против вызова сигнала, как если бы это была обычная функция в Qt

97

Допустим, у меня есть такой сигнал:

signals:
    void progressNotification(int progress);

Я только недавно узнал о ключевом слове emit в Qt. До сих пор я использовал сигналы, просто вызывая их как обычную функцию. Так что вместо:

emit progressNotification(1000 * seconds);

Я бы написал:

progressNotification(1000 * seconds);

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

сашоальм
источник
17
+1 Никогда не знал emit, не нужен. Тем не менее, странно, что вы узнали о нем спустя emitмного времени после непосредственного вызова сигналов, поскольку система сигнальных слотов - одна из первых вещей, которые нужно узнать о Qt.
Кристиан Рау,

Ответы:

88

emitпросто синтаксический сахар. Если вы посмотрите на предварительно обработанный вывод функции, которая излучает сигнал, вы увидите, что emitего просто нет.

«Магия» происходит в сгенерированном коде для функции испускания сигналов, на которую вы можете взглянуть, проверив код C ++, сгенерированный moc.

Например, fooсигнал без параметров генерирует эту функцию-член:

void W::foo()
{
    QMetaObject::activate(this, &staticMetaObject, 0, 0);
}

И код emit foo();предварительно обрабатывается, чтобы простоfoo();

emitопределяется в Qt/qobjectdefs.h(во всяком случае, в версии исходного кода с открытым исходным кодом), например:

#ifndef QT_NO_EMIT
# define emit
#endif

(Функция define Guard позволяет вам использовать Qt с другими фреймворками, которые имеют no_keywordsконфликтующие имена, с помощью параметра конфигурации QMake.)

Мат
источник
14
Знаете ли вы, была ли когда-либо реализация (или запланированная реализация), emitкоторая на самом деле больше, чем ничего? Я обнаружил, что наличие `` синтаксического сахара '' в этом случае просто сбивает с толку новичка (или, по крайней мере, меня, когда я был начинающим пользователем Qt) - похоже, что с emitпсевдоключевым словом происходит что-то волшебное или важное , когда оно ничего не делает в all - вся магия происходит в обычной старой функции, которая mocсоздает ( mocэто магия для сигналов и слотов Qt). emitэто ненужное украшение, которое только кажется важным.
Майкл Берр,
12
Эмиссия - это не просто украшение. emitсообщает человеку, читающему вызов, о том, что вот-вот произойдет волшебство (т. е. это вызовет запуск кода в объектах, о которых этот класс потенциально никогда не слышал, и эти вызовы могут быть синхронными или асинхронными), что по существу полностью теряется, если вы пропустите ключевое слово. Используй это. Это автоматическое документирование. «Новичкам» следует читать документы и учебные пособия, и они emitвсегда есть (по крайней мере, в официальных документах). Обнаружение того, что вы можете просто вызвать функцию, должно произойти после того, как вы «увидели свет» - вы уже не новичок.
Мат,
19
Хм, я не уверен, что согласен с вами в том, насколько ценным является emit"ключевое слово". Я думаю, что предпочел бы использовать соглашение об именах, если есть необходимость прояснить, что вызов функции является сигналом.
Майкл Берр
2
Что ж, я радикально с этим не согласен :) Принуждение к соглашению об именах - это то, что вы можете сделать сами в своих проектах / на рабочем месте, Qt этому не препятствует. Qt не заставляет вас использовать «ключевое слово» и даже позволяет вам отключить его, если оно конфликтует с другими частями вашего кода. На мой взгляд, подход с использованием ключевых слов лучше - компилятор не может помочь вам обеспечить соблюдение политик именования, но он обнаружит орфографические ошибки emit.
Мат,
15
Чтобы быть ясным - я не выступал за использование соглашения об именах - просто если причина для emitкомментария-псевдо-ключевого слова заключалась в том, чтобы прояснить, что сигнал вызывается, тогда соглашение об именовании могло бы сделать то же самое, без тайна и с аналогичными преимуществами. Qt не mocможет обеспечить соблюдение соглашения об именах (на самом деле, может обеспечить его соблюдение, но я тоже не защищаю это), но Qt не может принудительно использовать emitни то, ни другое. И хотя вы можете «выключить», emitесли возникнет конфликт имен, это не очень поможет, если у вас есть куча исходных файлов, которые его используют (без нужды для загрузки).
Майкл Берр
2

Спустя 18 месяцев ... Я начал с комментариев под ответом @Mat, и мне быстро не хватало места. Итак, ответ.

IMO не emitявляется ни синтаксическим сахаром, ни простым ключевым словом в том смысле, что

  1. Он генерирует код (как объяснено @Mat выше),
  2. Это помогает connectмеханизм признать , что на самом деле это signalи
  3. Это делает ваш сигнал частью «более крупной» системы, где сигналы и ответы (слоты) могут выполняться синхронно или асинхронно или ставиться в очередь, в зависимости от того, где и как был испущен сигнал. Это чрезвычайно полезная функция системы сигнал / слот.

Вся система сигнал / слот - это идиома, отличная от простого вызова функции. Я считаю, что это проистекает из модели наблюдателя. Существует также большая разница между a signalи a slot: сигнал не должен быть реализован, тогда как слот должен быть !

Вы идете по улице и видите горит дом (сигнал). Вы набираете 911 ( соедините пожарный сигнал со слотом ответа 911 ). Сигнал только излучали , а слот реализовала пожарная служба. Может быть неточно, но идею вы поняли. Давайте посмотрим на пример ОП.

Некоторый серверный объект знает, какой прогресс был достигнут. Так что это могло просто emit progressNotification(...)сигнализировать. Класс, который отображает фактический индикатор выполнения, должен выбрать этот сигнал и выполнить его. Но как представление связано с этим сигналом? Добро пожаловать в систему сигналов / слотов Qt. Теперь можно представить себе класс менеджера (обычно своего рода виджет), который состоит из объекта представления и объекта вычисления данных (оба они QObjects), которые могут выполнять connect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress).

Давайте не будем вдаваться в аспекты дизайна класса менеджера, но достаточно сказать, что именно здесь система сигнал / слот проявляет себя. Я могу сосредоточиться на разработке очень чистой архитектуры для своего приложения. Не всегда, но часто я обнаруживаю, что просто отправляю сигналы, а использую слоты .

Если возможно использовать / вызвать метод сигнала, не испуская его , то это обязательно означает, что вам никогда не нужна эта функция в качестве сигнала .

NameRakes
источник
6
Нет, emitэто действительно всего лишь пустой макрос и совершенно необязательный. Не так являются ключевыми словами signalи slotкоторые обрабатываются МЦЕМ. signalиспользуется для обеспечения реализации функции, slotиспользуется для создания записи метаобъекта, чтобы ее можно было найти с помощью SLOT(MySlot())макроса или в QML. emitсинтаксически наводит на размышления. Никто никогда не будет жаловаться, если вы напишете emit i++;(но, возможно, ваши коллеги), а у вас все еще нет связи i++.
derM 03
-5

Второй вариант подразумевает, что вы всегда знаете, каковы имя функции и параметры функции, и что объект, которому вы его отправляете, известен этой конкретной функции. Эти два случая не всегда верны, так что это две основные причины, по которым были созданы слоты и сигналы. «Под капотом» механизм сигналов и слотов - это просто таблица с указателями на каждую подключенную функцию.

Также посмотрите этот PDF-файл, который очень четко объясняет природу механизма сигналов и слотов: http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf

Эверт
источник
Оба способа требуют знания имени сигнала и его параметров - вы его излучаете, как вы могли послать то, чего не знаете? Оба имеют одинаковую семантику, они идентичны.
Мат,
1
Может быть, вы испортили сигнальный вызов прямым вызовом слота? Но я должен признать, что сначала меня также интересовал заголовок вопроса, поскольку я никогда не знал, emitчто это просто запрет. Но даже в этом случае чтение тела вопроса должно было прояснить ситуацию, поэтому -1.
Кристиан Рау,