Pregunta

Esta pregunta ya se hizo en este foro, pero no entiendo el concepto.

Estaba leyendo y parece que la señal y las ranuras se implementan utilizando punteros de función, es decir, la señal es una función grande que en su interior llama a todas las ranuras conectadas (punteros de función). ¿Es esto correcto? ¿Y cuál es el papel de los archivos moc generados en toda la historia? No entiendo cómo la función de señal sabe a qué ranuras llamar, es decir, qué ranuras están conectadas a esta señal.

Gracias por tu tiempo

¿Fue útil?

Solución

Qt implementa estas cosas de una manera que se asemeja a los lenguajes interpretados. Es decir. construye tablas de símbolos que asignan nombres de señal a punteros de función, los mantiene y busca el puntero de función por nombre de función cuando sea necesario.

Cada vez que emites una señal, es decir, escribe

emit something();

realmente llama a la función something () , la cual es generada automáticamente por el compilador de meta objetos y colocada en un archivo * .moc . Dentro de esta función, se comprueba a qué ranuras está conectada esta señal en este momento, y las funciones de ranura apropiadas (que usted implementó en sus propias fuentes) se llaman secuencialmente a través de las tablas de símbolos (de la manera descrita anteriormente). Y emit , al igual que otras palabras clave específicas de Qt, simplemente son descartadas por el preprocesador de C ++ después de que se generó * .moc . De hecho, en uno de los encabezados Qt ( qobjectdefs.h ), existen tales líneas:

#define slots 
#define signals protected
#define emit

La función de conexión ( connect ) simplemente modifica las tablas de símbolos mantenidas dentro de los archivos * .moc , y los argumentos que se le pasan (con SIGNAL () y las macros `SLOT] también están preprocesados ??para coincidir con las tablas.

Esa es la idea general. En su otra respuesta, & # 12472; & # 12519; & # 12540; & # 12472; nos proporciona enlaces a la lista de correo trolltech y a otra pregunta SO sobre este tema.

Otros consejos

Creo que debería agregar lo siguiente.

Hay otra pregunta vinculada , y hay < a href = "http://www.ntcore.com/files/qtrev.htm" rel = "nofollow noreferrer"> un muy buen artículo que se puede considerar como una expansión bastante detallada para su answer ; aquí está de nuevo este artículo , con mejoras (aunque todavía no es perfecto) resaltado de sintaxis de código.

Aquí está mi breve recuento, que puede ser propenso a errores)

Básicamente, cuando insertamos la macro Q_OBJECT en nuestra definición de clase, el preprocesador la expande a una declaración de instancia estática QMetaObject , una que sería compartida por todas las instancias de misma clase:

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

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

}

Esta instancia, a su vez, en la inicialización almacenará las firmas ( " methodname (argtype1, argtype2) " ) de las señales y las ranuras, ¿qué permite implementar la llamada a indexOfMethod () , que devuelve, bueno, el índice del método por su cadena de firma:

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

Ahora, cuando moc crea el archivo moc_headername.cpp para el encabezado de la clase Qt headername.h , coloca allí las cadenas de firma y otros datos que son necesarios para la correcta inicialización de la estructura d , y luego escribe el código de inicialización para el singleton staticMetaObject utilizando estos datos.

Otra cosa importante que hace es la generación del código para el método qt_metacall () , que toma la identificación del método de un objeto y una serie de indicadores de argumentos y llama al método a través de un largo cambiar de esta manera:

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

Por último, para cada señal moc se genera una implementación, que contiene una llamada QMetaObject :: active () :

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

Finalmente, la llamada a connect () traduce las firmas del método de cadena a sus identificadores enteros (los utilizados por qt_metacall () ) y mantiene una lista de señal-a conexiones de slots; cuando se emite la señal, el código enable () pasa por esta lista y llama al objeto apropiado " slots " a través de su método qt_metacall () .

Para resumir, la instancia QMetaObject estática almacena la metainformación " " (cadenas de firmas de métodos, etc.), un método qt_metacall () generado proporciona una tabla de métodos " eso permite que cualquier señal / ranura sea llamada por un índice, las implementaciones de señal generadas por moc usan estos índices a través de ctiv () , y finalmente connect () hace el trabajo de mantener una lista de mapas de índice de señal a ranura.

* Nota: hay una complicación de este esquema utilizado para el caso en el que queremos entregar señales entre diferentes hilos (sospecho que uno tiene que mirar el código recycling_activate () ), pero Espero que la idea general siga siendo la misma.

Este es mi entendimiento muy aproximado del artículo vinculado, que fácilmente puede estar equivocado, así que recomiendo ir y leerlo directamente)

PS. Como me gustaría mejorar mi comprensión de la implementación de Qt, ¡hágame saber cualquier inconsistencia en mi recuento!


Ya que mi otra respuesta (anterior) fue eliminada por un editor celoso, adjuntaré el texto aquí (me faltan algunos detalles que no se incorporaron en la publicación de Pavel Shved, y dudo que la persona quien eliminó la respuesta importada.)

@Pavel Shved:

  

Estoy bastante seguro de que en algún lugar de los encabezados Qt existe una línea:

     

#define emit

Solo para confirmar: lo encontré en el antiguo código Qt de Google Code Search. Es bastante probable que todavía esté allí); la ruta de ubicación encontrada era:

ftp://ftp.slackware-brasil.com.br & # 8250; slackware-7.1 & # 8250; contrib & # 8250; kde-1.90 & # 8250; qt-2.1.1.tgz & # 8250; usr & # 8250; lib & # 8250; qt-2.1.1 & # 8250; src & # 8250; núcleo & # 8250; qobjectdefs.h


Otro enlace complementario: http: //lists.trolltech .com / qt-interest / 2007-05 / thread00691-0.html - vea la respuesta de Andreas Pakulat


Y aquí hay otra parte de la respuesta: Pregunta Qt: ¿Cómo funcionan las señales y las ranuras?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top