12
votes

Automatiser la pimpl'ing de classes C ++ - Y a-t-il un moyen facile?

PIMPL's est une source de chaudière dans beaucoup de code C ++. Ils semblent être le genre de chose qu'une combinaison de macros, de modèles et peut-être un peu d'aide externe pourrait résoudre, mais je ne suis pas sûr de ce que le moyen le plus simple serait. J'ai vu des modèles qui aident à faire une partie de la levée mais Pas grand chose - vous finissez toujours par avoir besoin d'écrire des fonctions de transfert pour chaque méthode de la classe que vous essayez d'envelopper. Y a-t-il un moyen plus facile?

J'imagine un outil utilisé dans le cadre du processus de fabrication. Vous voulez que vos en-têtes publics soient des classes pimpl'd, vous fournissez donc un fichier d'entrée, disons pimpl.in, qui répertorie les classes (implémentées non pimpl'd) que vous souhaitez envelopper, alors ce fichier est examiné, Les classes PIMPL sont générées et seuls leurs en-têtes (pas les en-têtes de la classe d'origine) sont installés lors d'une «installation». Le problème est que je ne vois pas de moyen de le faire sans un analyseur complet en C ++, quelque chose que même les vendeurs compilateurs ne peuvent pas être corrects. Peut-être que les cours pourraient être écrits en quelque sorte qui facilite le travail d'un outil externe, mais je suis sûr que j'aurais finalement manquer toutes sortes de cas d'angle (par exemple, des classes modèles et / ou des fonctions des membres modèles).

Des idées? Est-ce que quelqu'un d'autre vient avec une solution pour ce problème déjà?


1 commentaires

Paresseux C ++ fait quelque chose de similaire.


3 Réponses :


0
votes

Une option est d'utiliser une classe d'interface à la place:

class ClassA {
  virtual void foo() = 0;
  static ClassA *create();
};

// in ClassA.cpp

class ImplA : public ClassA {
/* ... */
};
ClassA *ClassA::create() {
  return new ImplA();
}


0 commentaires

6
votes

Non, il n'y a pas de réponse facile. :-( Je penserais avec presque tous les experts de OO en disant "la composition préférer la composition sur l'héritage", il y aurait un soutien linguistique pour rendre la composition beaucoup plus facile que l'héritage.


0 commentaires

2
votes

Je ne dis pas que c'est bien (juste quelque chose qui vous a rendu à l'esprit).
Mais vous pouvez expérimenter avec la surcharge de l'opérateur ->

#include <memory>
#include <iostream>

class X
{
    public:
    void plop()
    {
        std::cout << "Plop\n";
    }
};

class PimplX
{
    public:
        PimplX()
        {
            pimpl.reset(new X);
        }
        X* operator->()
        {
            return pimpl.get();
        }
    private:
        PimplX(PimplX const&);
        PimplX& operator=(PimplX const&);
        std::auto_ptr<X>    pimpl;
};   


int main()
{    
    PimplX      x;

    x->plop();
}


2 commentaires

C'est une idée intéressante, bien que vous finissez par appeler toujours des fonctions sur un objet X directement, ce qui signifie que vous devrez exposer l'en-tête et la définition de X, donc x ne sera pas totalement opaque. D'autre part, si vous faites pitié principalement pour ABI et que vous ne rendant pas la classe opaque (et que vous pouvez faire confiance aux utilisateurs ne choisiront pas simplement d'utiliser X directement à la place lorsque ses en-têtes sont disponibles), je pense que ce serait correct.


De plus, les fonctions définies à l'intérieur des définitions de classe en C ++ sont implicitement en ligne. Vous auriez besoin de déplacer le constructeur et l'opérateur de PIMPLX -> les définitions de la classe pour ne pas lier l'appelant à l'ABI de X.