Forum Programmation.c++ Aide sur du code C++ dans Qtopia

Posté par  (site web personnel, Mastodon) . Licence CC By‑SA.
Étiquettes : aucune
1
26
fév.
2014

Hello!

Je suis en train d'essayer de débugger l'application QtopiaMail de QtMoko qui n'arrive pas à lire les mails qui contiennent plusieurs parties dans leur message (une partie html et une partie txt par exemple).

Je m'y connais peu en C++, mais j'ai réussi à déterminer depuis quelle fonction sont crées les messages, la voici :

/*!
   Returns the QMailMessage defined by a QMailMessageId \a id from 
   the message store.
*/
QMailMessage QMailStore::message(const QMailMessageId& id) const
{
    // Resolve from overloaded member functions:
    AttemptResult (QMailStore::*func)(const QMailMessageId&, QMailMessage*, MailStoreReadLock&) const = &QMailStore::attemptMessage;

    QMailMessage msg;
    repeatedly<ReadAccess>(bind(func, this, cref(id), &msg), "message(id)");
    return msg;
}

On a donc QMailMessage la classe qui définit un message, QMailStore la classe qui permet d'enregistrer un message reçu et de le restaurer (justement avec cette fonction).

AttemptResult est un enum privé de QMailStore :

enum AttemptResult { Success = 0, Failure, DatabaseFailure };

repeatedly est une méthode privée de QMailStore définie ainsi, qui tente plusieurs fois de se connecter à la base de donnée :

bool repeatedly(FunctionType func, const QString &description) const;

Je voulais vous demander de l'aide pour comprendre ce que fait la ligne qui commence par AttemptResult dans le premier bout de code cité.

Est-ce que c'est bien la définition locale d'un pointeur de fonction qui prends trois arguments et qui pointe sur l'adresse de la fonction attemptMessage ?

Si c'est bien ça, je me demande également comment le compilateur c++ sait de quelle méthode attemptMessage il faut retrouver l'adresse, puisque cette méthode est définie de deux manière avec une première fois 3 arguments et la seconde fois 4 arguments. Est-ce qu'il le sait grâce à la définition du pointeur de fonction qui utilise justement 3 arguments ? Ça serait magique :D

Merci pour votre aide !

  • # Re: Aide sur du code C++ dans Qtopia

    Posté par  (site web personnel) . Évalué à 6. Dernière modification le 26 février 2014 à 15:37.

    Est-ce que c'est bien la définition locale d'un pointeur de fonction qui prends trois arguments et qui pointe sur l'adresse de la fonction attemptMessage ?

    Presque.
    C'est la définition locale d'un pointeur vers une fonction membre qui prends trois argument et qui pointe vers QMailStore::attemptMessage.

    comment le compilateur c++ sait de quelle méthode attemptMessage il faut retrouver l'adresse

    Vu que tu assignes vers un un pointeur qui a 3 arguments, il sait quelle overload choisir.

    Ça serait magique

    Juste du C++ :-)

  • # Quand sait-on qu'on est passé du côté obscur de la Force?

    Posté par  . Évalué à 2.

    Je n'ai pas assez d'expérience en C++ pour savoir si le code cité est un exemple de «ceinture noire C++ 18ème dan», ou bien de l'obfuscation. On a quand même, en trois lignes de code,

    1) Un pointeur de fonction interne overloadée et polymorphe, qui prend pour argument une ref constante, un pointeur, et une ref non constante, le tout vers des classes maisons—au passage, est-ce que la fonction attemptMessage est statique?
    2) un appel à la fonction "repeatedly" avec de la STL hardcore, avec au passage l'argument MailStoreReadLock& que je ne vois pas (peut-être est-il fourni par défaut dans QMailStore::attemptMessage?)

    Est-ce ce qu'il n'est vraiment pas spossible de faire tout ça plus simplement?

    • [^] # Re: Quand sait-on qu'on est passé du côté obscur de la Force?

      Posté par  (site web personnel) . Évalué à 3.

      Ce qui aurais été bien aurais été de pouvoir utilisé attemptMessage dans bind directement comme ça:

      repeatedly<ReadAccess>(bind(&QMailStore::attemptMessage, this, cref(id), &msg), "message(id)");

      Mais ça ne marche pas car attemptMessage est overloadé et donc il faut faire un case explicite vers le bon type pour résoudre quelle overload on prends. Par example, ce qui est fait dans le code original.

      Est-ce ce qu'il n'est vraiment pas spossible de faire tout ça plus simplement?

      Si c'est possible, en utilisant C++11. Avec une fonction lambda, on peu se passer de bind:

      repeatedly<ReadAccess>([&](MailStoreReadLock& l){ this->attemptMessage(id, &msg, l); },
                             "message(id)");
      • [^] # Re: Quand sait-on qu'on est passé du côté obscur de la Force?

        Posté par  . Évalué à 2.

        Mais ça ne marche pas car attemptMessage est overloadé et donc il faut faire un case explicite vers le bon type pour résoudre quelle overload on prend

        Huhu, ça veut dire que le fait d'overloader une function quelque part peut péter le code à l'endroit qui appelle la fonction quand on utilise bind? Ça ressemble à un argument très fort contre l'utilisation de bind, non? Comment peut-on savoir a priori si la fonction qu'on bind est overloadée ou pas, et si on a la garantie qu'elle ne le sera jamais? Si je n'ai pas compris de travers,

        auto fun_prt = &externalfunc;
        auto f = bind(fun_prt, 3);
        

        peut marcher pendant des années, jusqu'au jour ou quelqu'un overloadera externalfunc? Ça me semble super bugogène ce truc, non?

        • [^] # Re: Quand sait-on qu'on est passé du côté obscur de la Force?

          Posté par  (site web personnel) . Évalué à 2.

          Oui exactement.
          Ajouter des overload n'est pas compatible source.

          Autre exemple:

          void func(Foo*);
          
          ///...
              func(0);

          Si je rajoute un overload je peux avoir des erreur. (si on rajoute func(Bar*) on a un appel ambigu (erreur de compilation), si on rajoute func(int) une autre fonction est appelée (possible bug))

          • [^] # Re: Quand sait-on qu'on est passé du côté obscur de la Force?

            Posté par  . Évalué à 2.

            Ajouter des overload n'est pas compatible source.

            Ah oui, moi j'étais juste resté sur les problèmes de cast automatique (style void func(double) qui peut être appelée func(3) ou func(3.0) sans problème, jusqu'au jour où elle est overloadée en void func(int)), qui ne posent problème que si le code est crade.

            Je ne connais rien aux contraintes des compilos et des problèmes de cohérence des langages, mais il ne serait pas plus sain d'avoir un mot clé, je ne sais pas «nooverload», pour justement garantir à l'utilisateur que l'API est stable en terme d'overload, et qu'il peut faire des pointeurs de fonction de manière stable?

            • [^] # Re: Quand sait-on qu'on est passé du côté obscur de la Force?

              Posté par  (site web personnel) . Évalué à 2.

              il ne serait pas plus sain d'avoir un mot clé, je ne sais pas «nooverload», pour justement garantir à l'utilisateur que l'API est stable en terme d'overload, et qu'il peut faire des pointeurs de fonction de manière stable?

              Tu peux rajouter dans ton .h

              static_assert(&func, "This function should not have overloads");

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.