Usando emit vs chamando um sinal como se fosse uma função regular no Qt

97

Digamos que tenho este sinal:

signals:
    void progressNotification(int progress);

Eu só aprendi recentemente sobre a palavra-chave emit no Qt. Até agora, eu costumava executar sinais apenas chamando-os como uma função normal. Então, em vez de:

emit progressNotification(1000 * seconds);

Eu escreveria:

progressNotification(1000 * seconds);

Chamá-los assim parecia funcionar, e todos os slots conectados seriam executados, então usar a palavra-chave emit causa um comportamento diferente ou é apenas um açúcar sintático?

sashoalm
fonte
17
+1 Nunca soube emitnão é necessário. É estranho, porém, que você aprendeu emitmuito depois de chamar sinais diretamente, já que o sistema de slots de sinal é uma das primeiras coisas a serem aprendidas sobre o Qt.
Christian Rau,

Respostas:

88

emité apenas açúcar sintático. Se você olhar para a saída pré-processada da função que emite um sinal, verá que emitsimplesmente desapareceu.

A "mágica" acontece no código gerado para a função de emissão de sinal, que você pode examinar inspecionando o código C ++ gerado pelo moc.

Por exemplo, um foosinal sem parâmetros gera esta função-membro:

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

E o código emit foo();é pré-processado para simplesmentefoo();

emité definido em Qt/qobjectdefs.h(pelo menos no tipo de código aberto da fonte), assim:

#ifndef QT_NO_EMIT
# define emit
#endif

(Definir guarda é para permitir que você use Qt com outros frameworks que têm nomes no_keywordsconflitantes por meio da opção de configuração do QMake.)

Esteira
fonte
14
Você sabe se já houve uma implementação (ou uma implementação planejada) de um emitque realmente fez mais do que nada? Eu acho que ter o 'açúcar sintático' neste caso apenas confunde o novato (ou pelo menos eu quando eu era um usuário novato do Qt) - parece que algo mágico ou importante está acontecendo com a emitpseudo-palavra-chave, quando ela não faz nada em all - toda a mágica acontece em uma função antiga regular que moccria ( mocé a mágica para sinais e slots Qt). emité uma decoração desnecessária que nada mais faz senão parecer importante.
Michael Burr de
12
Emit não é "apenas decoração". emitdiz à pessoa que está lendo a chamada que a mágica está para acontecer (ou seja, isso vai acionar o código em objetos dos quais essa classe potencialmente nunca ouviu falar e essas chamadas podem ser síncronas ou assíncronas), que é essencialmente totalmente perdido se você omitir a palavra-chave. Use-o. É autodocumentado. "Novatos" devem ler documentos e tutoriais e emitestão sempre lá (pelo menos nos documentos oficiais). Descobrir que você pode simplesmente chamar a função deve acontecer depois de "ver a luz" - você não é mais um novato nesse ponto.
Mat
19
Hmm, não tenho certeza se concordo com você sobre o valor da emit'palavra-chave'. Acho que teria preferido que uma convenção de nomenclatura fosse usada se fosse necessário deixar claro que uma chamada de função é um sinal.
Michael Burr
2
Bem, eu discordo radicalmente disso :) Forçar uma convenção de nomenclatura é algo que você mesmo pode fazer em seus projetos / local de trabalho, o Qt não impede isso. O Qt não força você a usar a "palavra-chave" e até permite que você desligue-o se entrar em conflito com outras partes do seu código. Em minha opinião, a abordagem de palavra-chave é melhor - o compilador não pode ajudá-lo a impor políticas de nomenclatura, mas detectará um erro ortográfico emit.
Mat
15
Para ser claro - eu não estava defendendo que uma convenção de nomenclatura fosse usada - apenas que se a razão para um emitcomentário de palavra-chave-psuedo fosse deixar claro que um sinal está sendo invocado, então uma convenção de nomenclatura poderia fazer o mesmo, sem mistério e com benefícios semelhantes. A convenção de nomenclatura não poderia ser imposta pelo Qt (na verdade, mocpoderia aplicá-la - mas não estou defendendo isso também), mas o Qt não pode impor o uso de emitqualquer um deles. E embora você possa 'desligar' emitse houver conflito de nomes, isso não ajuda muito se você tiver um monte de arquivos de origem que o estão usando (desnecessariamente, para inicializar).
Michael Burr,
2

Após 18 meses ... comecei com comentários sob a resposta de @Mat e estava ficando sem espaço rapidamente. Portanto, a resposta.

IMO emitnão é açúcar sintático nem uma palavra-chave simples no sentido de que

  1. Ele gera código (conforme explicado por @Mat acima),
  2. Ajuda o connectmecanismo a reconhecer que de fato é um signal, e
  3. Isso torna seu sinal parte de um sistema "maior", onde sinais e respostas (slots) podem ser executados de forma síncrona ou assíncrona, ou enfileirados, dependendo de onde e como o sinal foi emitido. Este é um recurso extremamente útil do sistema de sinal / slot.

Todo o sistema de sinal / slot é um idioma diferente de uma simples chamada de função. Acredito que decorra do padrão do observador. Há também uma grande diferença entre a signale a slot: um sinal não precisa ser implementado, mas um slot deve ser !

Você está andando na rua e vê uma casa pegando fogo (um sinal). Você disca 911 ( conecta o sinal de incêndio com o slot de resposta do 911 ). O sinal foi apenas emitido , enquanto o slot foi implementado pelo corpo de bombeiros. Pode ser impreciso, mas essa é a ideia. Vejamos o exemplo do OP.

Algum objeto de back-end sabe quanto progresso foi feito. Portanto, poderia simplesmente emit progressNotification(...)sinalizar. Cabe à classe que exibe a barra de progresso real, captar esse sinal e executá-lo. Mas como a visualização se conecta a esse sinal? Bem-vindo ao sistema de sinal / slot do Qt. Agora, pode-se conceber uma classe de gerenciador (normalmente um tipo de widget), que consiste em um objeto de visualização e um objeto de computação de dados (ambos sendo QObjects) connect (m_myDataEngine, &DataEngine::progressNotification, m_myViewObj, &SimpleView::displayProgress).

Não vamos entrar nos aspectos de design da classe de gerenciador, mas basta dizer que é aqui que o sistema de sinal / slot se destaca. Posso me concentrar em projetar uma arquitetura muito limpa para meu aplicativo. Nem sempre, mas muitas vezes, acho que apenas emito sinais, mas implemento slots .

Se for possível usar / chamar um método de sinal sem nunca emiti-lo , isso necessariamente implica que você nunca precisou dessa função como um sinal em primeiro lugar.

NameRakes
fonte
6
Não, na emitverdade é apenas uma macro vazia e puramente opcional. Não são assim as palavras-chave signale slotque são processadas pelo moc. signalé usado para fornecer a implementação da função, sloté usado para criar a entrada de metaobjeto para que seja encontrado com a SLOT(MySlot())macro ou em QML. emité suggar sintático. Nada jamais reclamará se você escrever emit i++;(mas talvez seus colegas de trabalho) e ainda não conseguir se conectar i++.
derM
-5

A segunda opção implicaria que você sempre sabe qual é o nome da função e os parâmetros da função e que o objeto para o qual você está enviando é conhecido por aquela função específica. Esses dois casos nem sempre são verdadeiros, então essas são as duas principais razões pelas quais slots e sinais foram criados. "por baixo do capô", o mecanismo de sinal e slot é apenas uma tabela com indicadores para cada função conectada.

Além disso, veja este pdf que explica muito claramente a natureza dos sinais e mecanismo de slots: http://www.elpauer.org/stuff/a_deeper_look_at_signals_and_slots.pdf

Evert
fonte
Ambas as formas exigem o conhecimento do nome do sinal e seus parâmetros - você está emitindo, como poderia emitir algo que não conhece? Ambos têm a mesma semântica também, são idênticos.
Mat
1
Talvez você esteja atrapalhando uma chamada de sinal com uma chamada direta de slot? Mas tenho que admitir que também me perguntei sobre o título da pergunta no início, já que nunca soube que emitera apenas um ambiente autônomo. Mas, mesmo neste caso, a leitura do corpo da pergunta deveria ter esclarecido as coisas, então -1.
Christian Rau,