IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Qt Discussion :

Q_OBJECT génere une erreur "undefined reference to `vtable for…`"


Sujet :

Qt

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut Q_OBJECT génere une erreur "undefined reference to `vtable for…`"
    Bonjour à tous.

    Je souhaite réaliser une simple boite de dialogue sous Qt 5.7.0.

    Néanmoins tous mes tests échouent.

    A force d'essais, je pense avoir fini par identifier l'origine du problème.

    Dans un simple code comme celui-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    #include <QApplication>
    #include <QDialog>
     
    class MyDialog: public QDialog
    {
        Q_OBJECT
     
        public:
        MyDialog(){}
     
        public slots:
     
        private:
     
    };
     
     
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
        MyDialog fenetre;
        fenetre.exec();
        return app.exec();
    }
    J'obtiens les erreurs suivantes :
    - undefined reference to `MyDialog::MyDialog()'
    - undefined reference to `vtable for MyDialog'

    Le problème semble venir de la macro Q_OBJECT. Si je la commente, plus d'erreur.

    Mon fichier .pro est ainsi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    QT += widgets
     
    SOURCES += \
        main.cpp

    Auriez vous une idée du problème ?

  2. #2
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 696
    Points : 2 438
    Points
    2 438
    Par défaut
    Bonjour.

    J'avais eu ce soucis également, c'est dû au fait que moc n'aime pas quand une classe est entièrement définie dans un header seul.
    Il faut impérativement le séparer en deux, header et implémentation.
    Je fais appel aux esprits de Ritchie, Kernighan, Stroustrup et Alexandrescu
    Donnez moi la force, donnez moi le courage de coder proprement !

    « Ça marche pas » n'est PAS une réponse convenable, merci de détailler le souci en fournissant l’environnement, le code source, les commandes et les messages d'erreur.

    Ce club possède également un clavardage, on y trouve quelques perles entre deux sessions d'entraides.

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut
    Tout ceci multiplie les fichiers à gérer, mais effectivement quand c'est séparé, ça passe correctement. Dommage, je trouvais que cette forme était bien pratique pour une malheureuse boite de dialogue, par exemple.

    Donc, dorénavant, un .h et un .cpp.

    Merci beaucoup pour cette aide, car j'ai perdu pas mal de temps à tenter de comprendre le pourquoi du comment.

  4. #4
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 696
    Points : 2 438
    Points
    2 438
    Par défaut
    Je suis d'accord, mais c'est le moc qui le veut.

    Cependant Qt a commencé à se séparer du moc et à le remplacer par d'autres outils.
    Reste à voir si ça va être mieux.

    (Si tu n'as pas d'autres questions, tu peux mettre le sujet en résolu.)
    Je fais appel aux esprits de Ritchie, Kernighan, Stroustrup et Alexandrescu
    Donnez moi la force, donnez moi le courage de coder proprement !

    « Ça marche pas » n'est PAS une réponse convenable, merci de détailler le souci en fournissant l’environnement, le code source, les commandes et les messages d'erreur.

    Ce club possède également un clavardage, on y trouve quelques perles entre deux sessions d'entraides.

  5. #5
    Membre habitué
    Homme Profil pro
    Inscrit en
    Juillet 2010
    Messages
    107
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juillet 2010
    Messages : 107
    Points : 189
    Points
    189
    Par défaut
    Slt,
    tu peux tout faire dans le main.cpp mais il faut ajouter à la fin du main.cpp :
    src : http://doc.qt.io/qt-5/moc.html

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut
    Pas si simple.

    Je l'avais fait, mais j'obtiens l'erreur suivante : main.moc: No such file or directory


  7. #7
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Le moc ne parse que les headers par défaut. Pour faire cela, il faut forcer le moc à parser main.cpp pour qu'il générer le main.moc.

    c'est dû au fait que moc n'aime pas quand une classe est entièrement définie dans un header seul.
    Il faut impérativement le séparer en deux, header et implémentation.
    Pas de problème pour déclarer une classe uniquement dans les headers (ou que dans les sources, ou ce que tu veux). Il faut juste appeler correctement les outils de build.

    Cependant Qt a commencé à se séparer du moc et à le remplacer par d'autres outils.
    Reste à voir si ça va être mieux.
    Le moc est prévu pour ne pas être supprimé avant Qt 7 (oui, les devs de Qt parlent déjà de Qt 7 )
    C'est qmake qui est en cours de remplacement (QBS)

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut
    Pour faire cela, il faut forcer le moc à parser main.cpp pour qu'il générer le main.moc.
    ...
    Il faut juste appeler correctement les outils de build.
    Concrètement sur un cas comme celui-ci que faudrait-il que je fasse ?

    Merci.

  9. #9
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    En fait, le fait d'ajouter un include vers le moc comme l'a indique loupium serait suffisant pour que le moc parse le .cpp (c'est ce que l'on fait dans les tests unitaires de QtTest). Il faut juste relancer qmake.

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut
    Effectivement, en relançant qmake, ça fonctionne.

    Merci beaucoup.

  11. #11
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ceci dit, juste pour épiloguer un tout petit peu...

    Même pour "une bête boite de dialogue", on se retrouve assez facilement à rajouter des éléments visuels, des signaux auxquels on souhaite connecter certains slots et d'autres joyeusetés du genre. Or, il faut savoir que toute fonction dont le code se trouve à l'intérieur de la définition de la classe dont elle est membre est réputée comme étant une fonction inline.

    Ainsi, si j'ai un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MaClasse{
    public:
        void foo(){
            //on fait quelque chose directement ici
       }
       void bar();
    };
    /* mais bar est implémentée dans le *.cpp qui va bien sous la forme de */
    void MaClasse::bar(){
     
        // on fait quelque chose ici
    }
    la fonction membre foo sera réputée implicitement inline parce qu'elle se trouve directement dans la définition de MaClasse, alors que bar sera forcément considérée comme étant non inline (car son implémentation est séparée de l'endroit où elle est déclarée).

    Alors, bien sur, le compilateur reste le dernier juge à décider si il produit bel et bien un code inline ou non pour une fonction donnée. Mais, si une fonction est "assez grosse" pour demander beaucoup d'instructions sans arriver à la limite au delà de laquelle le compilateur décidera de ne pas l'inliner, ca peut très rapidement provoquer une augmentation substantielle de la taille de l'exécutable final (et ce, d'autant plus vite que la fonction risque d'être appelée souvent).

    Le tout, sans oublier que, si on fournit l'implémentation d'une fonction directement dans la définition de la classe, cela nous oblige à inclure les fichiers d'en-tête de toutes les classes auxquelles on a recours dans la fonction (car le compilateur doit impérativement pouvoir vérifier que les fonctions membres de ces classes que l'on appelle sont bel et bien disponibles), alors que, en séparant la définition de la classe et l'implémentation de la fonction, nous aurions peut être pu nous contenter d'une déclaration anticipée au niveau du fichier d'en-tête.

    Et ca (comprenez : le fait de ne pas profiter de la déclaration anticipé "à chaque fois que faire se peut"), c'est typiquement le genre de truc qui peut très rapidement occasionner des temps de compilation totalement invivables

    Tout cela pour dire qu'il faut généralement se méfier des raisonnements proche de "c'est un peu dommage de faire (quelque chose) juste pour (un truc particulier)". Bien souvent, ce raisonnement s'arrête "beaucoup trop tot" et nous empêche de nous rendre compte que l'on est occupé à tresser les mailles d'un filet dans lequel nous finiront tôt ou tard (et sans doute plus tôt qu'on ne le pense) par tomber.

    Maintenant, tant que vous ne me demanderez pas de passer deux heures à essayer de compiler votre projet afin de pouvoir vous aider, vous restez bien sur tout à fait libre d'organiser votre projet à votre guise
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2004
    Messages
    199
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Mars 2004
    Messages : 199
    Points : 103
    Points
    103
    Par défaut
    Vous avez mis le doigt sur un point très important dont je n'avais pas conscience.

    Etant débutant, c'est à partir d'un exemple que j'ai structuré ce code, sans comprendre que de facto cela impliquait une fonction inline. En fait ça me semblait plus pratique que de gérer un fichier d'entête et un fichier de définition. Mais comme d'habitude, les choses sont beaucoup plus compliquées qu'elles n'y paraissent...

    Après étude du sujet, votre remarque est très pertinente et je pense en comprendre les implications.

    Merci koala01.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 13
    Dernier message: 17/05/2010, 21h28
  2. Réponses: 1
    Dernier message: 19/07/2009, 22h37
  3. undefined reference to `vtable for Graphique'
    Par lilly91 dans le forum Débuter
    Réponses: 11
    Dernier message: 23/06/2009, 17h41
  4. undefined reference to `vtable for
    Par zalalus dans le forum Qt
    Réponses: 13
    Dernier message: 27/05/2009, 15h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo