Sur cette page

Utilisation de Meta-Object Compiler (moc)

L'outil 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 produit un fichier source C++ contenant le code méta-objet de ces classes. Le code des méta-objets est notamment nécessaire pour le mécanisme des signaux et des créneaux, les informations sur les types d'exécution et le système de propriétés dynamiques.

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

qmake et CMake génèrent tous deux des makefiles avec des règles de compilation qui invoqueront moc en conséquence, de sorte que vous n'aurez pas besoin d'utiliser directement moc. qmake ajoutera ces règles de compilation par défaut, tandis qu'avec CMake, vous pouvez utiliser la propriété AUTOMOC pour gérer moc automatiquement. Pour plus d'informations sur moc, voir Pourquoi Qt utilise-t-il Moc pour les signaux et les slots ?

L'utilisation

moc est typiquement utilisé avec un fichier d'entrée contenant des déclarations de classe comme celle-ci :

class MyClass : public QObject
{
    Q_OBJECT

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

signals:
    void mySignal();

public slots:
    void mySlot();
};

En plus des signaux et des slots présentés ci-dessus, moc implémente également les propriétés des objets, 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 dans la classe à utiliser dans le système de propriétés.

Dans l'exemple suivant, nous déclarons une propriété du type d'énumération Priority qui est également appelée priority et qui possède une fonction get priority() et 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 les énumérations qui doivent être utilisées comme des drapeaux, c'est-à-dire être combinées par OU. Une autre macro, Q_CLASSINFO(), vous permet d'attacher 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();
};

La sortie produite par moc doit être compilée et liée, tout comme le reste du code C++ de votre programme ; sinon, la compilation échouera lors de la phase de liaison finale. 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 invoquer moc pour les fichiers qui contiennent une macro Q_OBJECT. De la même manière, lorsque vous définissez AUTOMOC à ON, CMake analysera les fichiers d'en-tête et les fichiers source au moment de la compilation et invoquera moc en conséquence.

Si la déclaration de classe se trouve dans le fichier myclass.h, la sortie moc doit être placée dans un fichier appelé 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 construction du programme.

Écrire des règles d'exécution pour l'invocation moc

À l'exception des programmes de test les plus simples, il est recommandé d'automatiser l'exécution du programme moc. En ajoutant quelques règles au fichier 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 makefiles qui effectuent toutes les manipulations nécessaires sur moc.

Si vous souhaitez créer vos makefiles vous-même, voici quelques conseils sur la manière d'inclure la gestion de moc.

Pour Q_OBJECT les déclarations de classes dans les fichiers d'en-tête, voici une règle de fichier de compilation utile si vous n'utilisez que GNU make :

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

Si vous voulez écrire de manière portable, vous pouvez utiliser des règles individuelles de la forme suivante :

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

Vous devez également vous rappeler d'ajouter moc_foo.cpp à votre variable SOURCES (remplacez votre nom préféré) et moc_foo.o ou moc_foo.obj à votre variable OBJECTS.

Les deux exemples supposent que $(DEFINES) et $(INCPATH) s'étendent vers les options define et include path qui sont transmises au compilateur C++. Ces options sont requises par moc pour prétraiter les fichiers sources.

Bien que nous préférions nommer nos fichiers source C++ .cpp, vous pouvez utiliser toute 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 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 le moc avant de compiler foo.cpp. Vous pouvez alors placer

#include "foo.moc"

à la fin de foo.cpp, où toutes les classes déclarées dans ce fichier sont connues.

Options de la ligne de commande

Voici les options de ligne de commande supportées par le moc :

OptionDescription de l'option
-D<macro>[=<def>]Définit la macro, avec une définition optionnelle.
-EPrétraitement uniquement ; ne génère pas de code de méta-objet.
-f[<file>]Forcer la génération d'une déclaration #include dans la sortie. C'est la valeur par défaut pour les fichiers d'en-tête dont l'extension commence par H ou h. Cette option est utile si vous avez des fichiers d'en-tête qui ne suivent pas les conventions de dénomination standard. La partie <file> est facultative.
-FdirmacOS. Ajoutez le répertoire du framework dir à la tête de la liste des répertoires dans lesquels les fichiers d'en-tête doivent être recherchés. Ces répertoires sont intercalés avec ceux spécifiés par les options -I et sont analysés dans un ordre de gauche à droite (voir la page de manuel de gcc). Normalement, il faut utiliser -F /Library/Frameworks/.
-hAffichez l'utilisation et la liste des options.
-iNe génère pas de déclaration #include dans la sortie. Ceci peut être utilisé pour exécuter le moc sur un fichier C++ contenant une ou plusieurs déclarations de classes. Vous devez alors #include le code du méta-objet dans le fichier .cpp.
-I<dir>Ajouter 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 a Q_PLUGIN_METADATA spécifié, la paire clé-valeur sera ajoutée à ses méta-données. Elle se retrouvera dans l'objet Json qui sera résolu pour le plugin au moment de l'exécution (accessible à partir de QPluginLoader). Cet argument est typiquement utilisé pour baliser les plugins statiques avec des informations résolues par le système de construction.
-nwNe pas générer d'avertissement (non recommandé). (Non recommandé.)
-o<file>Écrire la sortie sur <file> plutôt que sur la sortie standard.
-p<path>Oblige le moc à ajouter <path>/ au nom du fichier dans la déclaration #include générée.
-U<macro>Définir la macro.
@<file>Lire les 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 dans le 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 de moc.

Vous pouvez explicitement demander au 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.

Les diagnostics

moc vous avertira d'un certain nombre de constructions dangereuses ou illégales dans les déclarations de classe de Q_OBJECT.

Si vous obtenez des erreurs de liaison dans la phase finale de construction de votre programme, indiquant que YourClass::className() est indéfini ou que YourClass n'a pas de table virtuelle, c'est que quelque chose n'a pas été fait correctement. Le plus souvent, vous avez oublié de compiler ou de #include 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 réexécuter pour mettre à jour votre fichier makefile. Cela devrait suffire.

Systèmes de construction

Inclure les fichiers d'en-tête moc

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

Pour illustrer cela par un exemple, supposons que vous ayez deux en-têtes avec les fichiers sources correspondants : a.h, a.cpp, b.h, et b.cpp. Chaque en-tête possède 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 la macro (moc_a.cpp/moc_b.cpp), a.cpp, b.cpp, moc_a.cpp, et moc_b.cpp seront compilés séparément. Cela peut ralentir la 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 les fichiers, un seul fichier supplémentaire est généré par moc (appelons-le cmake.cpp pour les besoins de l'exemple). 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 le support moc de CMake à ce sujet, voir Inclure des fichiers d'en-tête moc dans les sources.

Limitations

moc ne gère pas tout le C++. Le principal problème est que les modèles de classe ne peuvent pas avoir la macro Q_OBJECT. Voici un exemple :

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

signals:
    void mySignal(int);
};

Les constructions suivantes sont illégales. Toutes ces constructions ont des alternatives que nous pensons être meilleures, donc la suppression de ces limitations n'est pas une grande priorité pour nous.

L'héritage multiple exige que QObject soit le premier

Si vous utilisez l'héritage multiple, moc suppose que la première classe héritée est une sous-classe de QObject. Assurez-vous également que seule la première classe héritée est 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 signaux ou de slots

Dans la plupart des cas où vous envisagez d'utiliser des pointeurs de fonction comme paramètres de signaux ou de slots, nous pensons que l'héritage est une meilleure alternative. Voici un exemple de syntaxe illégale :

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 est parfois préférable de remplacer le pointeur de fonction par l'héritage et les fonctions virtuelles.

Les Enums 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 complètement 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, les classes imbriquées ne peuvent pas avoir de signaux :

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 ou d'emplacements.

Voici un exemple de la construction incriminée :

class A
{
public:
    class B
    {
        Q_OBJECT

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

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

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

Seuls les signaux et les slots peuvent figurer dans les sections signals et slots d'une classe.

moc se plaindra si vous essayez de mettre dans les sections signals ou slots d'une classe d'autres constructions que les signaux et les slots.

Voir aussi Meta-Object System, Signals and Slots, et Qt's Property System.

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