Sur cette page

Utilisation du générateur de code ( Meta-Object Compiler, moc)

Le générateur de code MOC ( Meta-Object Compiler), moc, est le programme qui gère les extensions C++ de Qt.

L'outil moc lit un fichier d'en-tête C++. S'il trouve une ou plusieurs déclarations de classe contenant la macro Q_OBJECT, il génère un fichier source C++ contenant le code méta-objet de ces classes. Le code méta-objet est notamment nécessaire au fonctionnement du mécanisme des signaux et des slots, aux informations de type à l'exécution et au système de propriétés dynamiques.

Le fichier source C++ généré par moc doit être compilé et lié à l’implémentation de la classe.

qmake et CMake génèrent tous deux des fichiers makefile contenant des règles de construction qui invoqueront moc en conséquence ; vous n’aurez donc pas besoin d’utiliser directement moc. qmake ajoutera ces règles de construction par défaut, tandis qu’avec CMake, vous pouvez utiliser la propriété AUTOMOC pour gérer automatiquement moc. Pour plus d’informations générales sur moc, consultez la rubrique « Pourquoi Qt utilise-t-il Moc pour les signaux et les slots ? ».

Utilisation

moc est généralement utilisé avec un fichier d’entrée contenant des déclarations de classes comme celle-ci :

class MyClass : public QObject
{
    Q_OBJECT

public:
    MyClass(QObject *parent = 0);
    ~MyClass();

signals:
    void mySignal();

public slots:
    void mySlot();
};

Outre les signaux et les slots présentés ci-dessus, ` moc ` implémente également des propriétés d’objet, comme dans l’exemple suivant. La macro ` Q_PROPERTY()` déclare une propriété d’objet, tandis que ` Q_ENUM()` déclare une liste de types d’énumération au sein de la classe afin qu’ils soient utilisables dans le système de propriétés.

Dans l’exemple suivant, nous déclarons une propriété de type énumération Priority, qui s’appelle également priority et dispose d’une fonction get priority() et d’une fonction set setPriority().

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority)

public:
    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)

    MyClass(QObject *parent = 0);
    ~MyClass();

    void setPriority(Priority priority) { m_priority = priority; }
    Priority priority() const { return m_priority; }

private:
    Priority m_priority;
};

La macro Q_FLAG() déclare des énumérations destinées à être utilisées comme indicateurs, c’est-à-dire combinées par un opérateur OR. Une autre macro, Q_CLASSINFO(), vous permet d’associer des paires nom/valeur supplémentaires au méta-objet de la classe :

class MyClass : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("Author", "Oscar Peterson")
    Q_CLASSINFO("Status", "Active")

public:
    MyClass(QObject *parent = 0);
    ~MyClass();
};

Le code généré par moc doit être compilé et lié, tout comme le reste du code C++ de votre programme ; sinon, la compilation échouera lors de la phase finale de liaison. Si vous utilisez ` qmake`, cela se fait automatiquement. Chaque fois que ` qmake ` est exécuté, il analyse les fichiers d’en-tête du projet et génère des règles Make pour appeler ` moc ` pour les fichiers contenant la macro ` Q_OBJECT `. De même, lorsque vous définissez AUTOMOC sur ` ON`, CMake analysera les fichiers d’en-tête et les fichiers source au moment de la compilation et appellera ` moc ` en conséquence.

Si la déclaration de classe se trouve dans le fichier myclass.h, la sortie de moc doit être placée dans un fichier nommé moc_myclass.cpp. Ce fichier doit ensuite être compilé comme d’habitude, ce qui donne un fichier objet, par exemple moc_myclass.obj sous Windows. Cet objet doit ensuite être inclus dans la liste des fichiers objets qui sont liés entre eux lors de la phase finale de compilation du programme.

Rédaction de règles Make pour l’exécution moc

Pour tout programme autre que les programmes de test les plus simples, il est recommandé d’automatiser l’exécution de l’ moc. En ajoutant quelques règles au makefile de votre programme, make peut se charger d’exécuter moc lorsque cela est nécessaire et de gérer la sortie de moc.

Vous pouvez utiliser CMake ou qmake pour générer des fichiers Makefile qui gèrent toutes les opérations nécessaires liées à l’ moc.

Si vous souhaitez créer vous-même vos fichiers makefile, voici quelques conseils pour y intégrer la gestion de moc.

Pour les déclarations de classes « Q_OBJECT » dans les fichiers d’en-tête, voici une règle de fichier Makefile utile si vous utilisez uniquement GNU make :

moc_%.cpp: %.h
        moc $(DEFINES) $(INCPATH) $< -o $@

Si vous souhaitez écrire du code portable, vous pouvez utiliser des règles individuelles sous la forme suivante :

moc_foo.cpp: foo.h
        moc $(DEFINES) $(INCPATH) $< -o $@

Vous devez également penser à ajouter moc_foo.cpp à votre variable SOURCES (remplacez par le nom de votre choix) et moc_foo.o ou moc_foo.obj à votre variable OBJECTS.

Ces deux exemples partent du principe que $(DEFINES) et $(INCPATH) sont développés en options de chemin de définition et d’inclusion transmises au compilateur C++. Ces informations sont requises par moc pour pré-traiter les fichiers source.

Bien que nous préférions nommer nos fichiers source C++ .cpp, vous pouvez utiliser n’importe quelle autre extension, telle que .C, .cc, .CC, .cxx et .c++, si vous le souhaitez.

Pour les déclarations de classes « Q_OBJECT » dans les fichiers d’implémentation (.cpp), nous vous suggérons une règle de makefile comme celle-ci :

foo.o: foo.moc

foo.moc: foo.cpp
        moc $(DEFINES) $(INCPATH) -i $< -o $@

Cela garantit que make exécutera moc avant de compiler foo.cpp. Vous pouvez ensuite placer

#include "foo.moc"

à la fin de ` foo.cpp`, une fois que toutes les classes déclarées dans ce fichier sont entièrement connues.

Options de ligne de commande

Voici les options de ligne de commande prises en charge par moc :

OptionDescription
-D<macro>[=<def>]Définit une macro, avec une définition facultative.
-EPré-traitement uniquement ; ne génère pas de code méta-objet.
-f[<file>]Force la génération d'une instruction « #include » dans la sortie. C'est le comportement par défaut pour les fichiers d'en-tête dont l'extension commence par « H » ou « h ». Cette option est utile si vous disposez de fichiers d'en-tête qui ne respectent pas les conventions de nommage standard. La partie « <file> » est facultative.
-FdirmacOS. Ajoute le répertoire de frameworks dir en tête de la liste des répertoires à parcourir pour rechercher des fichiers d'en-tête. Ces répertoires sont intercalés avec ceux spécifiés par les options -I et sont parcourus de gauche à droite (voir la page de manuel de gcc). En règle générale, utilisez -F /Library/Frameworks/
-hAffiche l’utilisation et la liste des options.
-iNe génère pas d’instruction ` #include ` dans la sortie. Cette option peut être utilisée pour exécuter moc sur un fichier C++ contenant une ou plusieurs déclarations de classes. Vous devez ensuite utiliser ` #include ` pour générer le code méta-objet dans le fichier ` .cpp `.
-I<dir>Ajoute le répertoire `dir` au chemin d’inclusion des fichiers d’en-tête.
-M<key=value>Ajouter des métadonnées supplémentaires aux plugins. Si une classe dispose d’une déclaration ` Q_PLUGIN_METADATA `, la paire clé-valeur sera ajoutée à ses métadonnées. Celle-ci se retrouvera dans l’objet JSON qui sera résolu pour le plugin lors de l’exécution (accessible via ` QPluginLoader`). Cet argument est généralement utilisé pour marquer les plugins statiques avec des informations résolues par le système de construction.
-nwNe génère aucun avertissement. (Non recommandé.)
-o<file>Écrire la sortie dans ` <file> ` plutôt que dans la sortie standard.
-p<path>Fait en sorte que le moc ajoute « <path>/ » au début du nom de fichier dans l’instruction « #include » générée.
-U<macro>Supprime la définition de la macro.
@<file>Lit des options de ligne de commande supplémentaires à partir de <file>. Chaque ligne du fichier est traitée comme une option unique. Les lignes vides sont ignorées. Notez que cette option n’est pas prise en charge au sein du fichier d’options lui-même (c’est-à-dire qu’un fichier d’options ne peut pas « inclure » un autre fichier).
-vAfficher le numéro de version d’ moc.

Vous pouvez explicitement indiquer à moc de ne pas analyser certaines parties d’un fichier d’en-tête. moc définit le symbole de préprocesseur Q_MOC_RUN. Tout code entouré de

#ifndef Q_MOC_RUN
    ...
#endif

est ignoré par moc.

Diagnostics

moc vous avertira de la présence d’un certain nombre de constructions dangereuses ou illégales dans les déclarations de classes d’ Q_OBJECT.

Si vous obtenez des erreurs de liaison lors de la phase finale de compilation de votre programme, indiquant que ` YourClass::className() ` n’est pas défini ou que ` YourClass ` ne dispose pas d’une table de fonctions virtuelles (vtable), c’est qu’une erreur s’est produite. Le plus souvent, vous avez oublié de compiler ou d’ #include r le code C++ généré par moc, ou (dans le premier cas) d’inclure ce fichier objet dans la commande de liaison. Si vous utilisez qmake, essayez de le relancer pour mettre à jour votre makefile. Cela devrait résoudre le problème.

Systèmes de compilation

Inclusion des fichiers d'en-tête moc

qmake et CMake se comportent différemment en ce qui concerne l’inclusion des fichiers d’en-tête moc.

Pour illustrer cela par un exemple, supposons que vous disposiez de deux en-têtes avec les fichiers source correspondants : a.h, a.cpp, b.h et b.cpp. Chaque en-tête comporte une macro Q_OBJECT:

// a.h
class A : public QObject
{
    Q_OBJECT

    public:
        // ...
};
// a.cpp
#include "a.h"

// ...

#include "moc_a.cpp"
// b.h
class B : public QObject
{
    Q_OBJECT

    public:
        // ...
};
// b.cpp
#include "b.h"

// ...

#include "moc_b.cpp"

Avec qmake, si vous n’incluez pas le fichier généré par moc (moc_a.cpp/moc_b.cpp), les fichiers a.cpp, b.cpp, moc_a.cpp et moc_b.cpp seront compilés séparément. Cela peut ralentir les processus de compilation. Si vous incluez les fichiers générés par moc, seuls a.cpp et b.cpp devront être compilés, car le code généré par moc est inclus dans ces fichiers.

Avec CMake, si vous n’incluez pas ces fichiers, un seul fichier supplémentaire est généré par moc (appelons-le cmake.cpp pour les besoins de l’exemple). La commande ` cmake.cpp ` inclurait à la fois ` moc_a.cpp ` et ` moc_b.cpp`. L’inclusion du fichier généré par moc est toujours autorisée avec CMake, mais elle n’est pas nécessaire.

Pour plus d’informations sur la prise en charge de moc par CMake concernant ce sujet, consultez la section « Inclusion des fichiers d’en-tête moc dans les sources ».

Limites

moc ne prend pas en charge l’ensemble du langage C++. Le principal problème réside dans le fait que les modèles de classe ne peuvent pas comporter la macro ` Q_OBJECT `. Voici un exemple :

class SomeTemplate<int> : public QFrame
{
    Q_OBJECT
    ...

signals:
    void mySignal(int);
};

Les constructions suivantes sont illégales. Elles ont toutes des alternatives que nous estimons généralement meilleures ; la suppression de ces limitations n’est donc pas une priorité pour nous.

L’héritage multiple nécessite que QObject soit la première classe

Si vous utilisez l’héritage multiple, ` moc ` suppose que la première classe héritée est une sous-classe de ` QObject`. Veillez également à ce que seule la première classe héritée soit une ` QObject`.

// correct
class SomeClass : public QObject, public OtherClass
{
    ...
};

L’héritage virtuel avec QObject n’ est pas pris en charge.

Les pointeurs de fonction ne peuvent pas être des paramètres de signal ou de slot

Dans la plupart des cas où vous envisageriez d’utiliser des pointeurs de fonction comme paramètres de signal ou de slot, nous pensons que l’héritage constitue une meilleure alternative. Voici un exemple de syntaxe non valide :

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(void (*apply)(List *, void *), char *); // WRONG
};

Vous pouvez contourner cette restriction de la manière suivante :

typedef void (*ApplyFunction)(List *, void *);

class SomeClass : public QObject
{
    Q_OBJECT

public slots:
    void apply(ApplyFunction, char *);
};

Il peut parfois être encore plus judicieux de remplacer le pointeur de fonction par l’héritage et des fonctions virtuelles.

Les énumérations et les typedefs doivent être entièrement qualifiés pour les paramètres de signaux et de slots

Lorsqu’il vérifie les signatures de ses arguments, ` QObject::connect()` compare littéralement les types de données. Ainsi, ` Alignment ` et ` Qt::Alignment ` sont traités comme deux types distincts. Pour contourner cette limitation, veillez à qualifier entièrement les types de données lors de la déclaration des signaux et des slots, ainsi que lors de l’établissement des connexions. Par exemple :

class MyClass : public QObject
{
    Q_OBJECT

    enum Error {
        ConnectionRefused,
        RemoteHostClosed,
        UnknownError
    };

signals:
    void stateChanged(MyClass::Error error);
};

Les classes imbriquées ne peuvent pas avoir de signaux ni de slots

Voici un exemple de construction non conforme :

class A
{
public:
    class B
    {
        Q_OBJECT

    public slots:   // WRONG
        void b();
    };
};

Les types de retour des signaux/slots ne peuvent pas être des références

Les signaux et les slots peuvent avoir des types de retour, mais ceux qui renvoient des références seront traités comme renvoyant `void`.

Seuls les signaux et les slots peuvent apparaître dans les sections « signals » et « slots » d’une classe

moc générera une erreur si vous essayez d'insérer d'autres constructions que des signaux et des slots dans les sections ` signals ` ou ` slots ` d'une classe.

Voir également Système de méta-objets, Signaux et slots, et Système de propriétés de Qt.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.