Journal Visiteurs en C++

Posté par . Licence CC by-sa
27
24
avr.
2013

Sommaire

Le design pattern du Visiteur est un des plus connu. Il permet, selon Wikipédia, de «séparer un algorithme d'une structure de données». Je ne rappelle pas l'intérêt de ce design pattern, j'en viens directement au fond de cet article : quelle est la meilleure façon de faire un Visiteur en C++ ? Il y a essentiellement deux manières, et une variante sur la deuxième, avec chacune leurs avantages et inconvénients.

Le Visiteur polymorphe

C'est la manière la plus simple et celle qu'on rencontre partout (comme sur l'article Wikipédia). Voilà comment ça se présente sur un petit exemple.

class Visitor;

class Base {
public:
  virtual void accept(Visitor& vis) {
    vis.visitBase(*this);
  }
};

class Derived : public Base {
public:
  virtual void accept(Visitor& vis) override {
    vis.visitDerived(*this);
  }
};

class Visitor {
  virtual void visitBase(Base& b) {
  }

  virtual void visitDerived(Derived& d) {
  }
};

Du classique. Ensuite, quand on veut utiliser un Visiteur, on dérive de Visitor et hop, ça marche. Je ne détaille pas les avantages/inconvénients tout de suite.

Le Visiteur template

Pour ceux qui n'aiment pas le polymorphisme (qui est quand même l'essence de l'OO), C++ permet de faire un Visiteur avec des templates. Ce code est inspiré de LLVM (qui utilise la variante présentée ensuite) qui utilise des Visiteurs pour quasiment tout.

Le Visiteur template à base de RTTI

L'idée est simple : remplacer le dispatch du accept par une suite de condition vérifiant le type de l'objet dynamiquement. Pour cela, on va utiliser l'opérateur typeid qui permet de récupérer un objet de type type_info qu'on peut comparer à un autre objet de type type_info. Quand les objets sont simples (sans méthode virtuelle), le type_info peut être calculé à la compilation. Dès que l'objet possède une méthode virtuelle (le destructeur par exemple), le type_info est déterminé à l'exécution.

class Base {
public:
  virtual ~Base() { } // on met une fonction virtuelle pour rendre typeid dynamique

  void accept(Visitor& vis) {
    if (typeid(*this) == typeid(Derived)) {
      vis.visitDerived(static_cast<Derived&>(*this));
      return;
    }
    vis.visitBase(*this);
  }
};

class Derived : public Base {
  // pas besoin de code dans les classes filles
};

Reste un problème : en l'état actuel, le Visiteur doit encore être déclaré comme précédemment, avec du polymorphisme. C'est là qu'intervient le template. Il est impossible en C++ de déclarer une méthode virtuelle avec un template (heureusement !), mais ici, notre méthode accept n'est plus virtuelle, donc on peut lui coller un template.

  template<typename Visitor>
  void accept(Visitor& vis) {
    if (typeid(*this) == typeid(Derived)) {
      vis.visitDerived(static_cast<Derived&>(*this));
      return;
    }
    vis.visitBase(*this);
  }

Cette fois, c'est gagné, on peut déclarer n'importe quel visiteur sans devoir hériter d'un visiteur père.

class FooVisitor {
  void visitBase(Base& b) {
  }

  void visitDerived(Derived& d) {
  }
};

On doit cependant déclarer toutes les méthodes requises. Inutile de créer un Visiteur père avec des implémentations par défaut, ça ne marchera pas. Au moment de l'instanciation, le compilateur n'ira pas chercher dans la classe mère. Pour pallier ce problème, on peut modifier un peu notre Base et notre Visiteur et placer la logique de dispatch dans le Visiteur.

class Base {
public:
  virtual ~Base() { }

  template<typename Visitor>
  void accept(Visitor& vis) {
    vis.visit(*this);
  }
};

class Derived : public Base {

};

template<typename Subclass>
class Visitor {
public:
  void visit(Base& base) {
    if (typeid(base) == typeid(Derived)) {
      static_cast<Subclass*>(this)->visitDerived(static_cast<Derived&>(base)); // Ouch !
      return;
    }

    static_cast<Subclass*>(this)->visitBase(base);
  }

  // les implémentations par défaut

  void visitBase(Base& b) {
  }

  void visitDerived(Derived& d) {
  }

};

Un peu d'explication sur la ligne Ouch. Déjà, on a un argument template pour ce visiteur. Quand on implémentera un visiteur, il faudra dériver de celui-ci de la manière suivante :

class FooVisitor : public Visitor<FooVisitor> {
  // ...
};

Ça permet de prévenir Visitor de sa classe fille et donc de faire appel aux méthodes de la classe fille en castant this en Subclass. Comme on appelle visit depuis l'objet, on peut faire le dispatch dans le visiteur et appeler les méthodes utiles du Visiteur à ce moment-là. Ensuite, dans les Visiteurs (comme FooVisitor), on implémente uniquement les méthodes nécessaires et pas toutes les méthodes.

Avantages et inconvénients

Évidemment, tout est histoire de compromis. Passons donc en revue les avantages et inconvénients de chaque technique.

Du côté des avantages pour les templates, on peut citer le découplage entre le Visiteur et la structure, dans le sens où on n'a plus de cycle de dépendance entre Visitor et Base. Autre avantage, si on ajoute une classe dans la hiérarchie et qu'on n'a pas mis de méthode par défaut, on tombera dans le cas de Base. Ce cas ne sera sans doute pas pertinent, mais ça compilera. Avec le Visiteur polymorphe, il faudra ajouter la méthode adéquate dans le visiteur sinon, ça ne compilera pas.

Du côté des inconvénients, un petit test rapide montre que le Visiteur avec template est environ 3 fois plus lent que le Visiteur polymorphe. C'est là qu'intervient la variante pour améliorer ce manque de performance.

Le Visiteur template sans RTTI

Ce qui prend du temps, c'est le RTTI, c'est-à-dire tous les appels à typeid. Il est possible de mimer un genre de typeid en ajoutant un membre à Base qui sera initialisé dans les constructeurs.

enum Kind { BASE, DERIVED }

class Base {
public:
  const Kind kind;

  Base(Kind k) : kind(k) { } // pour les classes filles
  Base() : kind(BASE) { } // pour la classe Base

  // ...

};

class Derived : public Base {
public:
  Derived() : Base(DERIVED) { }
};

Ça alourdit un peu le code mais ça permet de se passer du RTTI. Du côté du Visiteur, on peut faire un simple switch:

template<typename Subclass>
class Visitor {
public:
  void visit(Base& base) {
    switch (base.kind) {
    case DERIVED:
      static_cast<Subclass*>(this)->visitDerived(static_cast<Derived&>(base)); // Ouch !
      return;
    case BASE:
      static_cast<Subclass*>(this)->visitBase(base);
    }
  }

  // ...

};

Avec cette astuce, on n'arrive pas tout à fait à la performance du Visiteur polymorphe mais on s'en approche à moins de 10%, ce qui est tout à fait convenable.

Les petits plus

LLVM, en plus de cette technique, introduit deux petits plus dont le premier n'est pas reproductible dans le cas du Visiteur polymorphe. Il s'agit de faire varier le type de retour du Visiteur. Actuellement, on a mis void. Mais on peut faire mieux et mettre le type de retour dans le template du visiteur.

class Base {
public:

  // ...

  template<typename Visitor>
  typename Visitor::return_type accept(Visitor& vis) {
    return vis.visit(*this);
  }
};

template<typename Subclass, typename RetTy>
class Visitor {
public:
  typedef RetTy return_type;

  return_type visit(Base& base) {
    switch (base.kind) {
    case DERIVED:
      return static_cast<Subclass*>(this)->visitDerived(static_cast<Derived&>(base)); // Ouch !
    case BASE:
      return static_cast<Subclass*>(this)->visitBase(base);
    }
  }

  return_type visitBase(Base& b) {
    return return_type();
  }

  return_type visitDerived(Derived& d) {
    return return_type();
  }
};

On peut maintenant faire des visiteurs qui renvoie des entier ou tout autre type. Dans le cas du Visiteur polymorphe, on peut passer par un membre mais c'est moins joli.

Autre petit plus inspiré de LLVM et qui là, peut tout à fait s'appliquer au Visiteur polymorphe. Généralement, on ne visite que les feuilles de l'arbre des classes et pas les classes abstraites. LLVM permet de visiter également une classe abstraite et, dans les implémentations par défaut, le visiteur appelle la méthode pour sa classe mère (éventuellement abstraite). Cette astuce permet de mutualiser certains traitements à toute une hiérarchie.

template<typename Subclass, typename RetTy>
class Visitor {
public:
  typedef RetTy return_type;

  // ...

  return_type visitBase(Base& b) {
    return return_type();
  }

  return_type visitDerived(Derived& d) {
    return visitBase(d);
  }
};

Conclusion

Voilà, le voyage dans le fabuleux monde des visiteurs C++ est fini. En conclusion, on peut dire qu'il n'y a pas un vainqueur net, mais qu'il faut adapter le type de Visiteur au projet.

  • # Utilité ?

    Posté par . Évalué à 9.

    Je ne rappelle pas l'intérêt de ce design pattern

    Dommage ça aurait pu rafraîchir la mémoire de pas mal de gens (moi le premier) !
    Pffff obligé de faire une recherche tout seul !! vie de m…

    Sinon, merci pour ce journal intéressant.

    kentoc'h mervel eget bezan saotred

  • # "Le visiteur polymorphe"

    Posté par . Évalué à 3.

    Ca ferait un excellent titre pour un bouquin de SF !
    Sinon concernant le lien j'aurai mis celui ) directement.

    • [^] # Re: "Le visiteur polymorphe"

      Posté par . Évalué à 5.

      Oui, je voulais mettre celui-là mais tu auras remarqué sur ton propre lien qu'il est faux à cause de la parenthèse finale. Et comme je ne me souvenais plus du code pour le mettre avec un % et qu'il était déjà très tard, j'ai fait au plus vite. Mais comme tout le monde arrive à tomber sur le bon lien assez vite, je me dis que j'ai bien fait de ne pas trop chercher. Et si un généreux admin peut le changer, ça sera nickel ;)

      • [^] # Re: "Le visiteur polymorphe"

        Posté par . Évalué à 2. Dernière modification le 25/04/13 à 14:59.

        je ne me souvenais plus du code pour le mettre avec un %

        Tu sélectionnes l'URL telle qu'affichée dans Firefox, qui s'occupe de translittérer l'URL avant de la placer dans le buffer de copie (il n'a ce comportement que si tu sélectionnes l'URL en entier, pas si tu en sélectionnes juste quelques caractères).

        • [^] # Re: "Le visiteur polymorphe"

          Posté par . Évalué à 3.

          Tu sélectionnes l'URL telle qu'affichée dans Firefox

          Du coup, ça marche moins bien avec Chromium ;)

  • # rtti lent

    Posté par (page perso) . Évalué à 1.

    Ce qui prend du temps, c'est le RTTI, c'est-à-dire tous les appels à typeid

    Quel compilateur utilises-tu? Je me souviens que l'implémentation de Microsoft était très lente, mais que d'autres s'en tirent mieux.

    http://devnewton.bci.im

    • [^] # Re: rtti lent

      Posté par (page perso) . Évalué à 5.

      Le seul élément d'une chaîne de compilation dans ce journal est LLVM, donc il doit utiliser Clang.

      Commentaire sous licence LPRAB - http://sam.zoy.org/lprab/

    • [^] # Re: rtti lent

      Posté par (page perso) . Évalué à 6.

      Un des problème de RTTI est que il grossi la taille du binaire,
      RTTI rajoute plein d'informations pour toutes les classes (même celles pour lesquelles on a pas besoin de RTTI)

      C'est pourquoi pas mal de projets compilent leur code avec -fno-rtti pour réduire la taille du binaire.

      Ça veux dire pas de typeid, pas de dynamic_cast, pas d'exceptions.

      • [^] # Re: rtti lent

        Posté par (page perso) . Évalué à 5.

        Je me souviens que j'avais remarqué qu'en utilisant juste -fno-exceptions (donc en gardant le rtti) ça reduisait déjà la taille de mon appli (après strip) de ~15% !

      • [^] # Re: rtti lent

        Posté par . Évalué à 3.

        Petite correction, avec -fno-rtti, on a quand même droit aux exceptions. Il faut ajouter -fno-exception pour enlever complètement les exceptions. Dans LLVM, il n'y a ni exception, ni RTTI activé. Mais ils ont quand même leur propre système de RTTI à base d'enum, comme montré ici, qui leur permet de faire un genre de dynamic_cast.

    • [^] # Re: rtti lent

      Posté par . Évalué à 3.

      J'ai essayé avec clang++ et g++ pour des résultats assez similaires. Ce qui ne m'étonne pas tant que ça vu que clang++ produit des binaires compatibles avec ceux de g++, donc utilise les mêmes structures pour type_info, et fait globalement la même chose.

  • # Compréhension

    Posté par . Évalué à 5.

    Je ne comprends pas très bien l'intérêt de faire le dispatche à la main alors que le système de type du langage permet de le faire de manière fiable.
    Le seul vrai avantage c'est le type de retour que l'on peut faire évoluer.

    J'ai l'impression que c'est surtout « pour ceux qui n'aiment pas le polymorphisme » (et de ce que tu dis tu ne gagne pas en performance avec la version template même sans RTTI). Bref à mon avis il s'agit surtout d'avoir un code plus complexe (et donc moins maintenable/fiable) et plus lent, pour un gain limité (disons précis si tu préfère).

    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Compréhension

      Posté par (page perso) . Évalué à 1.

      Parce que C++ ne propose qu'un simple dispatch à base de méthodes virtuelles?

      http://devnewton.bci.im

      • [^] # Re: Compréhension

        Posté par . Évalué à 6.

        Donc l'intérêt c'est de pouvoir avoir des types de retour différents (ce qui est décris dans le journal et dont je parle dans mon commentaire), cool.

        Mais en vrai ça sert si souvent que ça ?

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Compréhension

          Posté par (page perso) . Évalué à 0.

          Il y a plusieurs usages, mais le plus courant c'est de pouvoir implémenter des traitements sur une hiérarchie d'objets hors de ces objets.

          http://devnewton.bci.im

          • [^] # Re: Compréhension

            Posté par . Évalué à 4.

            Ça c'est le visiteur. Pas besoin de l'implémenter en template pour ça.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Compréhension

          Posté par (page perso) . Évalué à 2.

          Non, rien à voir. le type de retour dans ce cas là serait l'équivalent du type de retour de ta fonction virtuelle.

          L'interrêt ici est plutot de séparé le code et les données.

          Exemple fréquent dans un compilateur: tu as une classe de base (Statement) et des classe dérivée (IfStatement et ForStatement)

          Ensuite tu veux écrire des passe d'optimisation, tu pourrais aoir quelque chose comme ça:

          // Statement.h
          class Statement { 
            /*... */
            virtual void optimizeConstantPropagation() = 0;
            virtual void optimizeDeadCode() = 0;
          };
          // IfStatement.h
          class IfStatement : Statement {
            /* ... */
            virtual void optimizeDeadCode() {
               if (condition->isConstant()) { /*..remove one branch of the if .. */  }
               // recurse
               condition->optimizeDeadCode();
               thenBranch->optimizeDeadCode();
               elseBranch->optimizeDeadCode();
            }
          };
          
          

          On voit deux problème:
          1. la partie 'recurse' devra être répété dans toutes les passes d'optimisation.
          2. Si je veux créé une nouvelle passe d'optimisation, je dois rajouter une fonction dans toute les classes. Alors que justement, les passes d'optimisations devraient être modulaire et contenue dans un seul fichier.

          Le visiteur pattern permet de résoudre ces deux problèmes.

          • [^] # Re: Compréhension

            Posté par . Évalué à 4.

            Le visiteur pattern permet de résoudre ces deux problèmes.

            J'ai parlé de la version template. Le paterne en lui même je comprends tout à fais son intérêt.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Compréhension

          Posté par (page perso) . Évalué à 4.

          Pas uniquement en fait, ça permet aussi d’éviter l'ajout ( voir l'utilisation ) de méthodes virtuelles et les problèmes associés à celles-ci : ABI break, performance du à la vtable.

          La deuxième raison d'utilisation de ce pattern est une raison de coupling entre l'algorithm ( le Visitor ) et l'objet lui même.
          L'algo peut être instancié de manière différé, dérivé de manière autonome, ou encore avoir une association N <-> M avec l'objet en question.

          • [^] # Re: Compréhension

            Posté par . Évalué à 3.

            Pas uniquement en fait, ça permet aussi d’éviter l'ajout ( voir l'utilisation ) de méthodes virtuelles et les problèmes associés à celles-ci : ABI break, performance du à la vtable.

            Merci, là oui ça présente un intérêt (pas la version avec rtti).

            La deuxième raison d'utilisation de ce pattern est une raison de coupling entre l'algorithm ( le Visitor ) et l'objet lui même.
            L'algo peut être instancié de manière différé, dérivé de manière autonome, ou encore avoir une association N <-> M avec l'objet en question.

            Je n'ai vraiment pas du être clair, c'est la version template que je remet en cause pas le paterne.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Compréhension

      Posté par . Évalué à 3.

      Je ne dirais pas que ça complexifie le code. Avec un peu de préprocesseur, on parvient à générer un maximum de chose assez facilement. LLVM a d'ailleurs de ce point de vue des techniques assez intéressantes. Par exemple, dans le cas des instructions LLVM, ils ont un fichier dans lequel sont décrites toutes les instructions de manière assez simple. Et ils se servent de ce fichier pour générer plein de trucs dont le code de base du visiteur, que j'ai écris à la main ici mais dont on voit bien qu'il est assez répétitif. Ils le font pour plein de choses et c'est souvent fait de manière assez intelligente je trouve, et assez compréhensible.

      Pour les cas complexes, ils ont un autre outil qui s'appelle TableGen.

  • # C++ 2011 ?

    Posté par . Évalué à 2.

    est-ce que les divers ajouts du C++ 2011 permettent de simplifier l'écriture et/ou d'améliorer l'efficacité des versions template ?

    • [^] # Re: C++ 2011 ?

      Posté par (page perso) . Évalué à 4.

      Il utilise déjà C++11 (quand il écrit override)

      D'ailleurs, question style: est-ce que rajouter 'virtual' en plus de 'override' n'est pas un peu redondant. Personnellement, je n'écris pas virtual quand j'override.

      Je ne pense pas que le code template pourrait être simplifié. Peut-être que le typedef aurait pu être remplacé par sa nouvelle forme (prétendue plus cohérente)

      using return_type = RetTy;
      
      
      • [^] # Re: C++ 2011 ?

        Posté par . Évalué à 4.

        Je viens de découvrir override, mais je ne voie pas bien l’intérêt par rapport à virtual.

        Si quelqu’un veut bien prendre le temps de m’instruire, je lui serai reconnaissant.

        • [^] # Re: C++ 2011 ?

          Posté par . Évalué à 4.

          C'est la même chose qu'en Java: ça sert à détecter les surcharges involontaires. Par exemple

          struct Base {
              virtual void traiter_truc(Truc& truc);
          };
          
          struct Derived : Base {
              virtual void traiter_truc(Truc* truc);
          };
          
          

          Ça compile parfaitement, mais Derived::traiter_truc() ne redéfinit pas Base::traiter_truc(), et si on active pas les warnings qui vont bien dans son compilateur (qui sont chiants car parfois, c'est voulu), on s'en rend compte qu'à l'exécution… ou pas.

          Si Derived::traiter_truc était marqué en override, il y aurai une erreur de compilation.

        • [^] # Re: C++ 2011 ?

          Posté par . Évalué à 2.

          Si tu fais une erreur dans ta déclaration (typo, par exemple), tu n'overrides pas une fonction de la classe de base et le compilateur génèrera une erreur. Avec virtual, tu auras simplement déclaré une nouvelle fonction virtuelle…

          • [^] # Re: C++ 2011 ?

            Posté par (page perso) . Évalué à 2.

            C'était d'ailleurs un truc particulièrement pénible en C++ : le moindre changement (genre un const en plus ou en moins) et c'est une nouvelle fonction, pas une redéfinition de celle de la classe de base. J'ai quelques mauvais souvenirs de debug de programmes pour lesquelles la mauvaise fonction était appelée à cause de ça, alors que le compilateur aurait pointé l'erreur immédiatement avec « override ».

            • [^] # Re: C++ 2011 ?

              Posté par . Évalué à 2.

              Si tu utilise GCC ou Clang, compile avec -Woverloaded-virtual. Ça générera des warnings dans ce cas. Peut-être même un peu trop.

        • [^] # Re: C++ 2011 ?

          Posté par (page perso) . Évalué à 6.

          struct Base {
            virtual void helloWorld() const;
          };
          
          struct Derived1 : Base {
            virtual void helloWold(); // Ooops, j'ai oublié le const. Et ça compile sans problème.
                /* Bon, le test échoue. Combien de temps va-t-il me falloir pour trouver pourquoi ma fonction n'est pas apellée */
          };
          
          struct Derived2 : Base {
            void helloWorld() override;
          // error: ‘void Derived2::helloWorld()’ marked override, but does not override
             /* Une erreur de compile, 5 secondes pour la corriger. */
          };
          
          

          Très pratique aussi quand on fait du refactoring et que on renomme ou change les argument de helloWorld,
          dans ce cas, on a des erreur de compile sur le code qui n'a pas encore été mis à jour (et c'est bien).

          • [^] # Re: C++ 2011 ?

            Posté par . Évalué à 3.

            Merci à tous les trois (Batchyx, sn00py, Gof). Je ne voyais pas trop, et j’avais l’impression de faire un virtual différemment. En fait, c’est juste une information complémentaire pour le compilateur et éviter des erreurs bêtes.

            Encore merci, je peux poser le cerveau, j’ai appris un truc aujourd’hui ;-)

      • [^] # Re: C++ 2011 ?

        Posté par . Évalué à 6. Dernière modification le 24/04/13 à 14:12.

        Haaa :) Il y en a un qui l'a remarqué. Je l'ai pas mis pour faire du C++11 absolument mais pour voir si c'était pris en charge par la coloration C++. Et en fait, non.

        Quant à savoir si C++11 permet d'améliorer les choses, je dirais non sur ce plan là effectivement.

        D'ailleurs, question style: est-ce que rajouter 'virtual' en plus de 'override' n'est pas un peu redondant.

        Oui et non. Je sais que certains préconisent de ne pas mettre de virtual pour les réimplémentations de méthodes virtuelles, mais j'ai toujours trouvé ça très perturbant. Du coup, je l'ai toujours mis systématiquement. Maintenant, avec un override, ça ne serait sans doute pas nécessaire mais ça n'apporte pas la même information je trouve. Et comme override se situe en fin de ligne, c'est assez difficile d'avoir un aperçu de toutes les méthodes virtuelles. Tandis qu'avec un virtual systématique, on le voit mieux je trouve. Mais comme tu dis, c'est une question de style.

  • # Cocasse

    Posté par . Évalué à 3.

    Faire un article sur un design pattern dont on ne voit pas l'intérêt est déjà spécial (avec en plus pas d'exemple concret d'utilisation dans la vraie vie), mais en plus cette implémentation ressemble fortement a celle dans Modern C++ Design (qui pourrait être cité en référence aussi). Il me semble que celle de Modern C++ Design ne s’embête pas avec le type de retour.

    Je suis cependant d'accord sur la conclusion.

    • [^] # Re: Cocasse

      Posté par . Évalué à 6.

      Il n'a pas dis qu'il ne vois pas l'intérêt juste qu'il ne va pas en parler.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

    • [^] # Re: Cocasse

      Posté par . Évalué à 3.

      avec en plus pas d'exemple concret d'utilisation dans la vraie vie

      Tu as mal lu, j'ai mis un lien sur une utilisation dans LLVM (les compilateurs utilisent beaucoup ce design pattern).

      cette implémentation ressemble fortement a celle dans Modern C++ Design (qui pourrait être cité en référence aussi)

      Je n'ai jamais lu ce bouquin (je devrais peut-être) donc c'est assez difficile de le mettre en référence. Mais comme tu le cites ici, du coup, l'erreur est réparée ;)

  • # Dire que certaine pense que le Ocaml est illisible...

    Posté par . Évalué à 10. Dernière modification le 24/04/13 à 10:56.

    Quand on voit la complexité du truc…

    type base_t
    type derived_t
    type t = 
         | BASE of base_t * t list 
         | DERIVED of derived_t * t list
    
    let rec visitor accumulator t =
       match t with
           | [] -> List.rev accumulator
           | BASE (base_info, suite) :: tail 
                        -> let a  = visitor ((foo_base base_info) :: accumulator) tail in
                           visitor ((foo_base base_info) :: a) suite
           | DERIVED (derived_info, suite) :: tail 
                        -> let a  = visitor ((foo_base base_info) :: accumulator) tail in
                           visitor ((foo_derived derived_info) :: a) suite
    (*execution : *)
    let resultat_list = visitor [] my_data_tree in
     ...
    
    

    Et encore, dans le match on peut mettre des bouts d'arbres comme :
    | BASE (base_info, BASE (base_info2, []))

    Le petit point difficile est l'usage d'une liste dans le type, mais on peut utilisé un type Option pour faire la terminaison de récursion.
    Pour info :
    (item :: list) permet d'ajouter un item au début d'une list ou de décomposer une liste dans les matchs.
    (foo plop) permet d'appliquer la fonction foo au paramètre plop

    En conclusion, vive les types sommes et le "pattern matching" d'arbre.

    "La première sécurité est la liberté"

    • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

      Posté par . Évalué à 1.

      Pour le coup, je trouve qu'en essayant d'adapter au pied de biche un pattern objet à CaML, tu rends le CaML illisible. Je suis entièrement avec ta conclusion ceci-dit.

      • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

        Posté par . Évalué à 5.

        objet ou pas objet, le but est de pouvoir parcourir un arbre en séparant la définition de l'arbre et les parcours.

        Et pour ça, les types sommes et le "pattern matching" sont totalement imbattables. C'est tellement puissant que je ne comprends pas pourquoi cela n'a pas été ajouter au C++.

        C'est une très bonne alternative pour tous les cas ou il y a une pléthore de petit objet à gérer ensemble.

        "La première sécurité est la liberté"

        • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

          Posté par (page perso) . Évalué à 2.

          objet ou pas objet, le but est de pouvoir parcourir un arbre en séparant la définition de l'arbre et les parcours.
          Et pour ça, les types sommes et le "pattern matching" sont totalement imbattables. C'est tellement puissant que je ne comprends pas pourquoi cela n'a pas été ajouté au C++.

          Sur le même principe on a eut Scala dérivé de Java. C'est assez populaire mais la syntaxe en est quand même bien lourde, ce qui me fait sans hésiter retourner vers un langage fonctionnel dès que je le peux (Ocaml, Lisp ou Haskell).

          • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

            Posté par . Évalué à 3.

            Scala n'est pas du tout dérivé de java, il produit du code pour jvm, c'est tout. Et je suis d'accord que la syntaxe est encore pire que celle de Ocaml.

            "La première sécurité est la liberté"

            • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

              Posté par (page perso) . Évalué à 1. Dernière modification le 24/04/13 à 16:41.

              Scala n'est pas du tout dérivé de java, il produit du code pour jvm, c'est tout

              Mince alors ! On m'aurait menti?
              Wikipédia dit :

              Si on souhaite l'utiliser exclusivement avec la JVM, il est alors possible d'utiliser les bibliothèques écrites en Java de façon complètement transparente. Ainsi, Scala bénéficie de la maturité et de la diversité des bibliothèques qui ont fait la force de Java depuis une dizaine d'années. De plus, il est possible d'invoquer du code écrit en Scala à partir de programmes écrits en Java ce qui facilite la transition de Java à Scala.

              C'est comme ça qu'on me l'avait présenté et que je l'ai toujours vu. Je me coucherai moins bête ce soir. Merci pour la précision.

              • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                Posté par . Évalué à 4.

                Il est compatible c'est tout (comme groovy).

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                Posté par (page perso) . Évalué à 2.

                Ce que tu cites ne dit absolument pas qu'il y a un lien d'héritage entre scala et java, ça doit venir d'une mauvaise compréhension de java/jvm.
                Java est compilé en bytecode, qui s'exécute sur la jvm.
                Scala est compilé en bytecode qui s'exécute sur la jvm.
                C'est ce bytecode commun qui fait qu'il existe un pont, que tu peux utiliser des libs java en scala.

                • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                  Posté par . Évalué à -2.

                  Mouais… Enfin, Java et Scala sont quand même liés d'une certaine manière puisque Martin Odersky, qui est un des créateurs de Scala (si ce n'est leur chef de file), a énormément travaillé sur Java avant de créer Scala. Il fait notamment partie de ceux à l'origine de l'inclusion des generics dans Java et a bossé sur le compilateur Java.

                  De ce fait, il semble assez net que Java a largement influencé Scala. Alors certes, Scala n'est pas à proprement parler dérivé de Java dans le sens où il ne s'agit pas d'un superset de Java et que du code Java ne peut pas être compilé par le compilateur Scala mais l'histoire de Scala et Java sont quand même assez fortement liées.

                  • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                    Posté par . Évalué à 2.

                    Et Java est inspiré de C++, lui même inspiré du C. Quasiment tous les langages ont des inspiration. Tu as même une partie spécialement pour ça sur Wikipedia. Ce n'est pas parce que Java est l'un des inspirateur de scala que la syntaxe de scala doit être proche de celle de Java (elle ne l'est pas en fait).

                    Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                    • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                      Posté par . Évalué à 0.

                      On est totalement d'accord mais la question initiale était de dire si oui ou non on pouvait dire que Scala était un dérivé de Java, pas simplement de savoir s'ils avaient la même syntaxe ( "Sur le même principe on a eut Scala dérivé de Java" ).

                      En fonction de comment on interprète le mot "dérivé", on peut répondre oui ou non à cette question. Je dis ça parce que tout le monde a sauté sur l'aspect syntaxe/compilation alors qu'il me semble que ce n'est pas ce que voulait dire vlamy à l'origine. On retrouve certains des concepts de Java dans Scala (héritage simple et pas de possibilité de faire de l'héritage privé/protégé, type erasure, generics, fonctionnement sur une machine virtuelle …), en parallèle avec des concepts qui viennent d'ailleurs.

                      Après, je vais arrêter le troll là parce que ça devient du pinaillage, déjà que ma remarque initiale n'était pas mal en termes de pinaillage…

                      • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                        Posté par . Évalué à 2.

                        La citation initiale complète :

                        Sur le même principe on a eut Scala dérivé de Java. C'est assez populaire mais la syntaxe en est quand même bien lourde, ce qui me fait sans hésiter retourner vers un langage fonctionnel dès que je le peux (Ocaml, Lisp ou Haskell).

                        J'ai présumé, comme d'autres, que les deux phrases, se suivant au sein d'un même paragraphe étaient liées.

                        Je ne ferrais pas de remarques sur les concepts aux quels tu fais références et qui ne sont pas liés à Java.

                        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                        • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                          Posté par . Évalué à 3. Dernière modification le 25/04/13 à 00:43.

                          Je ne ferrais pas de remarques sur les concepts aux quels tu fais références et qui ne sont pas liés à Java.

                          Jolie prétérition… Mis à part ça tout le monde sait que Java gère l'héritage multiple, que l'héritage privé est possible, que les annotations de type sont préservées à l'exécution et que la plupart des compilateurs Java produisent du code natif…

                        • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                          Posté par (page perso) . Évalué à 2.

                          J'ai présumé, comme d'autres, que les deux phrases, se suivant au sein d'un même paragraphe étaient liées.

                          En fait je voulais parler de l'ensemble Java+Scala du coup. Ce qui serait équivalent à ajouter du fonctionnel dans C++ (pour revenir à la discussion de départ), plutôt que d'utiliser/développer un langage compatible mais indépendant (comme Scala du coup).

                          En gros : ma méconnaissance de Scala (que j'assimilais à Scala+Java) m'a fait pointé Scala comme un mauvais exemple, alors qu'à priori ce n'est pas le cas. Reste maintenant à voir ce que les gens font de Scala dans la vie de tous les jours, et je n'ai pas trop d'idée à ce sujet.

                          Je ne ferrais pas de remarques sur les concepts aux quels tu fais références et qui ne sont pas liés à similaires a ceux utilisés en Java , mais on ne peut pas en déduire une dérivation.

                      • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                        Posté par (page perso) . Évalué à 3.

                        J'avoue que j'ai lâché le mot « dérivé » un peu vite ! Mais l'esprit était là effectivement.
                        Je me rappelais juste que les gens de Scala avaient pour intention d'amener les dévs. Java au fonctionnel en facilitant la compatibilité entre les deux langages. Donc oui je persiste et je signe : il a existé un lien fort entre Java et Scala dès la sortie de ce dernier. Par contre, effectivement, parler de langage dérivé n'est pas approprié, d'où ma citation de Wikipédia qui me paraissait assez claire pour désambiguïser la chose.

                • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

                  Posté par (page perso) . Évalué à 3. Dernière modification le 25/04/13 à 09:46.

                  Ce que tu cites ne dit absolument pas qu'il y a un lien d'héritage entre scala et java, ça doit venir d'une mauvaise compréhension de java/jvm.

                  Ce n'est pas du tout ce que j'ai voulu laisser entendre. Je dis juste que quand on me l'a présenté (devrais-je préciser que c'était une présentation des auteurs du langage?) on a soutenu une volonté de compatibilité avec Java, qui aurait pour but d'amener en douceur les développeurs Java vers un langage fonctionnel (Scala donc). Ce qui ne veut pas dire effectivement que l'un est dérivé de l'autre, mais plutôt que Scala peut embarquer Java (ou l'inverse c'est à vous de voir).

                  C'est vrai que ce n'est pas évident de tout comprendre quand on appréhende Scala avec environnement Java+Scala. Ce qui était mon cas jusqu'à hier. C'est ce qui a motivé mon commentaire aussi.

                  En fait j'aurais dû mettre en exergue cette phrase :

                  De plus, il est possible d'invoquer du code écrit en Scala à partir de programmes écrits en Java ce qui facilite la transition de Java à Scala.

            • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

              Posté par . Évalué à 1.

              Je sens que je suis en train de nourrir le troll, mais
              Quels points spécifiques de la syntaxe Scala te font dire qu'elle est pire que celle de OCaml (je ne trouve pas la syntaxe de OCaml particulièrement horrible mais passons…) ?

    • [^] # Re: Dire que certaine pense que le Ocaml est illisible...

      Posté par . Évalué à 1.

      je vais peut-être dire une bêtise, mais est-ce qu'on ne pourrait pas utiliser un "fold" à la place du pattern matching ?

  • # De la réinvention de la roue.

    Posté par . Évalué à 1.

    Moi si on me disait aujourd'hui que je devais représenter une structure arborescente, et que je ne peux pas faire autrement (genre par exemple, j'ai vu des arborescences compliquées parcourues par des visiteurs, sauf que l'arborescence était statique…), je me poserait pas trop la question : boost::variant et boost::apply_visitor. Si quelqu'un à une autre méthode où le visiteur peut gérer plusieurs types avec une méthode template, je prend.

    Après reste la question de comment l'utiliser: un boost::variant<A,B,C...> avec A, B, C contenant des pointeurs vers des variants, ou boost::variant<std::unique_ptr<A>, std::unique_ptr<B>, std::unique_ptr<C> >. Chacun ayant ses inconvénients.

    Parce que bon, le coup de forcer ses types à être polymorphiques pour utiliser la RTTI ou de la réimplementer avec des énums, c'est un peu limite quand même.

    • [^] # Re: De la réinvention de la roue.

      Posté par . Évalué à 2.

      Parce que bon, le coup de forcer ses types à être polymorphiques pour utiliser la RTTI ou de la réimplementer avec des énums, c'est un peu limite quand même.

      Je pense que la question c'est plutôt comment s'est implémenté en boost.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: De la réinvention de la roue.

        Posté par . Évalué à 4.

        Boost::Variant, c'est une « union »¹ et un entier pour dire quel type est actuellement utilisé, rien d'extrêmement compliqué. C'est comme la réimplémentation du RTTI avec un énum, sauf que c'est vérifié par le compilo, qu'il n'y a pas d'héritage et que ça permet d'utiliser des templates dans le visiteur. Et en plus, le code est déjà écrit (sinon au pire c'est pas bien compliqué à réimplémenter pour des cas spécifiques).

        ¹ Pas vraiment une union, car Boost::Variant supporte aussi les types avec des destructeurs et des constructeurs par copie qui lancent des exceptions, de manière assez poilue.

  • # Avantage pas compris

    Posté par . Évalué à 1.

    J'ai compris l'article, même si je suis assez septique sur les raisons d'utiliser la version template… mais un des commentaires plus haut à répondu avec un assez pertinent: ABI break restreinte (faudra que je réfléchisse un peu au comment du pourquoi, par contre, je n'ai pas encore pris le temps de penser en détail).

    Par contre, il y a des "avantages" de la version template sur lesquels je suis vraiment dubitatif et ou j'aimerai une explication:

    Autre avantage, si on ajoute une classe dans la hiérarchie et qu'on n'a pas mis de méthode par défaut, on tombera dans le cas de Base. Ce cas ne sera sans doute pas pertinent, mais ça compilera. Avec le Visiteur polymorphe, il faudra ajouter la méthode adéquate dans le visiteur sinon, ça ne compilera pas.

    Justement, je trouve plutôt utile que le compilateur refuse de compiler quand le cas n'est pas pertinent, ça évite les bogues…

    Mais on peut faire mieux et mettre le type de retour dans le template du visiteur.

    Je ne vois vraiment pas l'intérêt? Quand j'ai eu besoin de visiteur, c'était pour effectuer un traitement sur les données (sur un arbre dans mon cas passé). Je ne parviens pas à voir de situation ou je voudrais récupérer une donnée sur l'acte de visiter, sachant que le type de donnée en question va varier en fonction de l'objet visité… enfin, je m'embrouille, mais bref, j'aimerai un exemple concret de l'intérêt de ce point?

    Autre petit plus inspiré de LLVM et qui là, peut tout à fait s'appliquer au Visiteur polymorphe.

    Pas vraiment un plus, puisque tu le dis toi-même, ça s'applique aussi à la version polymorphe?

    Si je résume, on aurai la version template qui permet d'éviter d'avoir à utiliser la RTTI et de casser l'ABI, et la version polymorphe qui elle offre de meilleures performances?

    • [^] # Re: Avantage pas compris

      Posté par . Évalué à 3.

      Justement, je trouve plutôt utile que le compilateur refuse de compiler quand le cas n'est pas pertinent, ça évite les bogues…

      Quand tu ajoutes une classe à ta hiérarchie, avec le visiteur polymorphe, tu dois déjà surcharger le accept. Bon, tu peux mettre un truc vide dans accept mais c'est un coup à l'oublier après (ça m'est déjà arrivé). Du coup, tu vas ajouter une méthode virtuelle au visiteur pour traiter cette nouvelle classe. Et il faudra ensuite ajouter toutes les surcharges dans tous les visiteurs. Enfin bref, tu te retrouves à devoir ajouter plein de code pour une simple petite classe.

      Avec la version template, sans rien faire dans le accept et dans le visit, ça compile, et ça tombe dans le cas de base. Après, tu ajoutes le cas qui va bien dans le visit, et là, ça va même marcher pour quelques visiteurs pour lesquels tu auras regroupé les traitements pour une des classes mères. Donc dans ce cas, avec très peu de code, ça fonctionne plutôt pas mal.

      Je ne vois vraiment pas l'intérêt?

      Disons que tu veux afficher tes structures, là pas de souci, tu peux utiliser le visiteur qui renvoie void. Ensuite, tu veux calculer un truc sur tes structures, tu peux vouloir un visiteur qui renvoie directement le résultat du calcul plutôt que de passer par un membre. Exemple concret : tu veux calculer le type d'une expression binaire, tu vas définir un visiteur qui renvoie un truc de type Type que tu vas appliquer à des trucs de type Expression. Tu vas d'abord récupérer le Type de l'expression de gauche (à l'aide du même visiteur), puis le type de l'expression de droite puis, en fonction de l'opérateur, tu vas calculer le type de l'expression binaire et le renvoyer. Ne pas utiliser de type de retour va complexifier considérablement ton code.

      Pas vraiment un plus, puisque tu le dis toi-même, ça s'applique aussi à la version polymorphe?

      C'est un plus dans le sens où ça se fait rarement sur les versions polymorphes (je n'ai jamais vu aucun exemple qui le faisait) mais que ça pourrait se faire.

      Si je résume, on aurai la version template qui permet d'éviter d'avoir à utiliser la RTTI et de casser l'ABI, et la version polymorphe qui elle offre de meilleures performances?

      En gros. C'est plus subtil, il y a aussi des considérations de style, de flexibilité, etc.

      • [^] # Re: Avantage pas compris

        Posté par . Évalué à 1.

        Ne pas utiliser de type de retour va complexifier considérablement ton code.

        Ajouter un attribut à ton visiteur avec un accesseur.

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: Avantage pas compris

          Posté par . Évalué à 2.

          Ajouter un attribut à ton visiteur avec un accesseur.

          Pas que. Comparons.

          Version avec le type de retour.

          Type *visitBinaryExpression(BinaryExpression& expr) {
            Type *left = expr.getLHS()->accept(*this);
            Type *right = expr.getRHS()->accept(*this);
            return ComputeType(left, right);
          }
          
          

          Version sans le type de retour.

          void visitBinaryExpression(BinaryExpression& expr) {
            expr.getLHS()->accept(*this);
            Type *left = getResult();
            expr.getRHS()->accept(*this);
            Type *right = getResult();
            Type *result = ComputeType(left, right);
            setResult(result);
          }
          
          

          Ben je préfère la première version dans ce cas, beaucoup plus lisible et compréhensible.

          • [^] # Re: Avantage pas compris

            Posté par . Évalué à 0.

            Tu codes tassé toi dis donc, ça coûte si cher que ça les lignes vides? :D

            Sinon, autre version, avec une méthode supplémentaire à implémenter:

            void visitBinaryExpression(BinaryExpression& expr) {
              Type *left = expr.getLHS()->MethodeSup(*this);
              Type *right = expr.getRHS()->MethodeSup(*this);
              Type *result = ComputeType(left, right);
              setResult(result);
            }
            
            class Type
            {
              virtual accept(fooBar*)=0;
              Type* MethodeSup(FooBar* foo)
              {
                accept(*foo);
                return getResult();
              }
            };
            
            

            Pour le coup, la lisibilité de visitBinaryExpression est la même, il n'y qu'une seule méthode supplémentaire a créer.

            Note que je suis pas sûr de la bonne équivalence du code, vu que ton getResult() ne semble lié à aucun objet. J'ai donc supposé un prototype genre "Type* TypeDerive::getResult(void)" mais ça reste une supposition.
            Si c'est lié à autre chose, le même principe peut se faire aussi de toute façon.

            Je pense aussi que tu vas lever l'argument de la méthode virtuelle pure qui empêche d'utiliser accept() sur Type, mais ce n'est pas parce qu'une méthode est virtuelle pure qu'il est interdit de l'implémenter. C'est juste les filles ont l'obligation de le faire (j'ai appris ça il y a quelques mois, j'ai testé vite fait et effectivement… mais je n'ai pas trop joué avec, pas eu l'utilité encore).

            PS: j'ai l'impression d'avoir merdé quelque part… alors je m'excuse par avance -.-'

          • [^] # Re: Avantage pas compris

            Posté par . Évalué à 2.

            Ben je préfère la première version dans ce cas, beaucoup plus lisible et compréhensible.

            Je ne dis pas le contraire. C'est juste que je ne trouve pas que c'est considérablement plus complexe au vu de la différence de complexité entre l'implémentation polymorphe et template.

            Là où ça deviens vraiment plus complexe c'est si tu souhaite paralléliser le calcul des sous résultats.

            Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: Avantage pas compris

              Posté par . Évalué à 2.

              Je ne dis pas le contraire. C'est juste que je ne trouve pas que c'est considérablement plus complexe au vu de la différence de complexité entre l'implémentation polymorphe et template.

              Je ne trouve pas l'une tellement plus complexe que l'autre. La complexité ne se trouve pas au même endroit, c'est tout.

              Là où ça deviens vraiment plus complexe c'est si tu souhaite paralléliser le calcul des sous résultats.

              Alors là, pour le coup, ça ne change strictement rien, ça dépendra essentiellement de l'état interne de ton visiteur (est-ce qu'on peut appeler le visiteur en même temps sur la branche droite et la branche gauche, pour reprendre l'exemple) et pas de la manière dont tu as implémenté ton visiteur (template ou polymorphisme).

              • [^] # Re: Avantage pas compris

                Posté par . Évalué à 1.

                Alors là, pour le coup, ça ne change strictement rien, ça dépendra essentiellement de l'état interne de ton visiteur (est-ce qu'on peut appeler le visiteur en même temps sur la branche droite et la branche gauche, pour reprendre l'exemple) et pas de la manière dont tu as implémenté ton visiteur (template ou polymorphisme).

                J'ai lancé ça un peu comme ça. Mais tu peut avoir des méthodes sans effet de bords dans la version template, alors que dans la version polymorphe tu es obligé d'en avoir.

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # un inconvénient des templates

    Posté par (page perso) . Évalué à 3.

    Un inconvénient des templates, c'est la difficulté de compréhension du code. Sur une échelle de 1 à 10 en compétence C++, je me note à disons 8, et j'ai toujours autant de mal à comprendre les templates dès qu'ils sont un peu subtils comme ici. Autant dire que pour le commun des mortels, c'est juste du charabia imbuvable. Donc à éviter si possible.

    Sinon, honnêtement, avant d'implémenter un visiteur, il faut commencer par se demander si on n'a pas un problème de design de l'architecture des classes. Et si on n'en a pas, attendre le lendemain matin, et se reposer la question une seconde fois…

    • [^] # Re: un inconvénient des templates

      Posté par (page perso) . Évalué à 10.

      C'est que tu te surnotes mon petit :). Le commun des développeurs C++ c'est plutôt vers 3 ou 4. Les templates ça n'a rien de difficile en soit, c'est juste que c'est une approche différente de l'approche 'OO avec classes' classiques.

      Et franchement, ça (les visiteurs), c'est facile par rapport à des choses comme boost::proto, boost::spirit, nt2, et autres trucs vraiment poilus. Bref, soit les gens qui développent ça sont à 17 sur 10, soit ton échelle de valeur est cassé :).

      • [^] # Re: un inconvénient des templates

        Posté par . Évalué à 7.

        Bref, soit les gens qui développent ça sont à 17 sur 10, soit ton échelle de valeur est cassé :).

        Ou alors l'échelle n'est pas linéaire. :)

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

        • [^] # Re: un inconvénient des templates

          Posté par . Évalué à 4. Dernière modification le 25/04/13 à 11:50.

          Ouais enfin soit il est super pointu sur tout ce qui ne touche pas aux templates, pointeur de fonction, surcharge d'opérateur (dont les new, cast, delete)), soit il n'a pas encore croisé du code un petit peut corsé. Un petit exemple dans le code que j'ai sous la main ( l'un des plus simple):

          remove_if( truc.begin(), truc.end(),boost::not1(boost::mem_fun_ref(&shared_ptr<Machin>::get)));
          
          

          Ce qui basiquement déplace à la fin du vecteur les élément vide ;)

          Dans le code sur lequel je bosse j'ai aussi du bind2nd qui traine un poil et quelques utilisations plus poussé avec imbrication, me faisant me poser des question sur la santé mentale du gars qui a écrit le bordel, et parfois je me fait peur en voyant mon nom dans le annotate, et je me bénis (ou maudit) lorsque les commentaires expliquent (ou pas) l'intention du schmilbik.

          Enfin tout ça, c'est l'usage c'est bien plus simple que l'écriture; fait toi peur en regardant les hpp de boost ;).

          Ou alors son échelle à un putain de palier nécessitant du matériel d'escalade ;)

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

          • [^] # Re: un inconvénient des templates

            Posté par (page perso) . Évalué à 6.

            J'ai modifié le commentaire pour faire apparaître le code qui était caché suite à une erreur lors de la déclaration du langage.

            « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

          • [^] # Re: un inconvénient des templates

            Posté par . Évalué à 4.

            Ou alors son échelle à un putain de palier nécessitant du matériel d'escalade ;)

            Je pense qu'on arrive au stade de la fusée la :)

          • [^] # Re: un inconvénient des templates

            Posté par . Évalué à 3.

            Je vois pas ce qu'il y a d'illisible dans ton code. Ce n'est pas parce qu'on connais pas sa bibliothèque standard¹ que c'est forcément illisible.

            Après si il y a ça sur trente lignes dans une seule fonction, son code est illisible, et il faut séparer ça en plusieurs fonctions. Tu ferais la même chose si tu voulais traduire ce code en C.

            ¹ Toutes les fonctionnalités de boost utilisées dans cette ligne sont dans C++11.

    • [^] # Re: un inconvénient des templates

      Posté par (page perso) . Évalué à 10.

      j'ai toujours autant de mal à comprendre les templates dès qu'ils sont un peu subtils comme ici

      Eugh ? qu'est-ce qu'il y a de subtile ? Ici on a affaire à tout ce qu'il y a de plus basic en matière de template. Pas de spécialisation partielle ou de lookup compliqués dans ces examples.

      À la limite, le seul petit truc un peu curieux est l'utilisation du Curiously Recurring Template Pattern.

      Bref, je ne te met pas 8/10 :-)

    • [^] # Re: un inconvénient des templates

      Posté par . Évalué à 4.

      Si ça c'est subtil, regarde pas de trop près les boost::algorithm ou même std::algorithm, ni les std::mem_fun_ref et consort.

      Sur les templates, coté difficulté celui là atteint péniblement 2 (les utilisations de base de vector ou string étant le niveau 1) (j'ai bien dit utilisation pas écriture ^ )

      Il ne faut pas décorner les boeufs avant d'avoir semé le vent

    • [^] # Re: un inconvénient des templates

      Posté par . Évalué à 0.

      j'ai toujours autant de mal à comprendre les templates dès qu'ils sont un peu subtils comme ici.

      Bah, autant dire que le C++ est imbittable, ce qui est probablement vrai. Ça fait quelques années que j'ai tendance à utiliser systématiquement des langages de haut niveau, et quand je vois des trucs comme ça, c'est clair que je n'ai aucune envie d'y retourner :-)

      Mais bon, il y a plus imbittable que le code C++: les erreurs retournées par le compilateur, avec un petit bonus quand la STL est impliquée et qu'il faut scroller dans le terminal pour retrouver le début de la dernière erreur…

      Après, imbittable ne veut pas dire inutile. En plus, la "bittabilité" est culturelle, le chinois est probablement aussi imbittable pour un européen que le français l'est pour un Chinois.

      • [^] # Re: un inconvénient des templates

        Posté par . Évalué à 4.

        Bah, autant dire que le C++ est imbittable, ce qui est probablement vrai. Ça fait quelques années que j'ai tendance à utiliser systématiquement des langages de haut niveau, et quand je vois des trucs comme ça, c'est clair que je n'ai aucune envie d'y retourner :-)

        Sauf qu'on aura toujours besoin de langage comme C/C++ pour implémenter des trucs qui doivent aller vite, comme les interpréteurs de langage haut niveau ou des compilateurs. Chacun son domaine.

        Mais bon, il y a plus imbittable que le code C++: les erreurs retournées par le compilateur, avec un petit bonus quand la STL est impliquée et qu'il faut scroller dans le terminal pour retrouver le début de la dernière erreur…

        Effectivement, tu n'as pas dû utiliser un compilateur C++ depuis bien longtemps et notamment clang parce qu'il y a eu de très très très gros progrès sur les messages d'erreur. clang a commencé et gcc est en train de s'y mettre.

        • [^] # Re: un inconvénient des templates

          Posté par . Évalué à 2.

          Sauf qu'on aura toujours besoin de langage comme C/C++ pour implémenter des trucs qui doivent aller vite, comme les interpréteurs de langage haut niveau ou des compilateurs. Chacun son domaine.

          C'est exactement ce que je disais à la fin de mon commentaire. Ceci dit, je pense qu'il reste une tendance à surutiliser le C++ dans des domaines où il n'est pas vraiment adapté—typiquement, clients "lourds" pour bureau, interfaces graphiques, etc. Une partie significative des petits jeux débiles livrés avec Gnome par exemple sont en C ou en C++, et je pense que c'est pareil sur toutes les plateformes.

        • [^] # Re: un inconvénient des templates

          Posté par . Évalué à 3.

          Sauf qu'on aura toujours besoin de langage comme C/C++ pour implémenter des trucs qui doivent aller vite, comme les interpréteurs de langage haut niveau ou des compilateurs. Chacun son domaine.

          Ou que non alors ! Lisaac a déjà prouvé que l'on peut être du niveau de smaltalk en terme d'expressivité et battre le C++ en terme de vitesse.

          OCaml est de trés haut niveau, mais les optimisations dans la génération de code sont minimes (pas de déroulage de boucle ou de spécialisation de fonction). Il est pourtant dans le top10 en terme de vitesse.

          "La première sécurité est la liberté"

          • [^] # Re: un inconvénient des templates

            Posté par . Évalué à 2.

            Je n'arrive a rien trouver d'autre que les articles wikipedia sur lisaac… le site officiel n'affiche qu'une page avec sa propre URL en titre centré gris, le wiki à l'air HS, et les seuls trucs que je trouve à son sujet sont les endroits ou l'on peut utiliser son source… pas envie de fouiller dans du code source inconnu la :)
            As-tu d'autres infos?

            Pour OCaml… les seules choses que je lise sur wikipedia au sujet de sa vitesse, c'est que ça dépasse les 50% de la vitesse du C, et le passage ou il est dit que ses fonctions sont plus rapides que celles des langages impératifs contiens le mot théoriquement. Je sais qu'on ne peut pas forcément prouver (benchmark? Y'a toujours quelqu'un pour les renier…) qu'un langage est plus rapide qu'un autre, mais je me demande de quel top 10 tu parles?

            En tout cas +1 pour avoir parlé de lisaac, il semble intéressant.

            • [^] # Re: un inconvénient des templates

              Posté par . Évalué à 2.

              Le dev de Lisaac est arreté. Le site est mort. Il y a eu un gros article sur les concepts manipulés publiés dans linux mag en début d'année dernière de mémoire.

              http://www.ed-diamond.com/produit.php?ref=lmag148&id_rubrique=1

              Comme benchmark :
              http://benchmarksgame.alioth.debian.org/u32/code-used-time-used-shapes.php

              Lisaac a été premier de ce benchmark quelque temps avant que les téchniques soient copié pour les code C et C++.

              "La première sécurité est la liberté"

              • [^] # Re: un inconvénient des templates

                Posté par (page perso) . Évalué à 2.

                Comme benchmark :
                http://benchmarksgame.alioth.debian.org/u32/code-used-time-used-shapes.php
                Lisaac a été premier de ce benchmark quelque temps avant que les téchniques soient copié pour les code C et C++.

                Un seul benchmark ne prouve pas grand chose. De plus, d'après la suite du commentaire, les différences ne viendraient pas forcément des optimisations permises par le langage mais l'algorithme utilisé (à moins que ce soit les optimisations qui étaient reprises dans g++). Haskell (enfin surtout ghc) arrive à battre le C (gcc) sur un ou deux benchmarks bien choisis, mais personne d'honnête n'irait prétendre que Haskell bat les performances de C.

                À un moment, il faut être réaliste aussi. Ça fait des années qu'un "smart enough compiler" va arriver au niveau des performances du C. En pratique, ce n'est pas encore le cas (même si je crois personnellement que c'est possible). Pas la peine de vendre du rêve! Peut être que Rust va arriver à quelquechose de ce point de vue là.

                • [^] # Re: un inconvénient des templates

                  Posté par . Évalué à 2.

                  Il n'y a pas un seul benchmark, mais une dizaine. L'algo est fixe.

                  Haskell vu sa gestion de la mémoire à forcément des cas pathologiques.

                  Pas la peine de vendre du rêve!

                  Lisaac battait le code C dans la plus part de tests donc bon. Le problème n'est pas de faire un compilo intelligent. Le problème est de faire un langage avec suffisamment de sémantique pour qu'un compilateur puisse faire un boulot intelligent. Par exemple, le C fixe le layout mémoire de ces structures de données, et 2 pointeurs de même type sont censé pouvoir pointé sur la même zone mémoire. Ces 2 caractéristiques peuvent être des killers de performances. En gros, c'est le boulot du codeur, et le compilo ne peut rien faire.

                  "La première sécurité est la liberté"

                  • [^] # Re: un inconvénient des templates

                    Posté par . Évalué à 2. Dernière modification le 25/04/13 à 16:06.

                    Lisaac battait le code C

                    Battait, ça veut dire que C a rattrapé Lisaac ensuite?

                    Et sinon, le C++ n'a pas ces problèmes il me semble, et pourtant les bench disent souvent la même chose: il est souvent plus lent que le C.

                    • [^] # Re: un inconvénient des templates

                      Posté par . Évalué à 1.

                      "Battait, ça veut dire que C a rattrapé Lisaac ensuite?"

                      Oui, car lisaac générant du C, c'est facile de comprendre pourquoi il est plus rapide. Au pire, tu copie/colle sa sortie et tu dis que c'est du C manuel.

                      "Et sinon, le C++ n'a pas ces problèmes il me semble, et pourtant les bench disent souvent la même chose: il est souvent plus lent que le C."

                      Dans le shootout, il est régulièrement premier grâce à openMP et les templates.

                      "La première sécurité est la liberté"

                      • [^] # Re: un inconvénient des templates

                        Posté par . Évalué à 0. Dernière modification le 25/04/13 à 16:17.

                        Attends… t'es en train de dire qu'un langage générant du C était plus rapide que le C?

                        Tu cherches à me faire bugger par une boucle infinie ou quoi? XD

                        • [^] # Re: un inconvénient des templates

                          Posté par . Évalué à 2.

                          Le C génére bien de l'assembleur.

                          Ces langages étant turing complet et bas niveau, on peut tous faire avec ou presque. Après, il y a une question de verbosité. Le code généré est plus gros, que ce tu ferais à la main, cela décale ton langage vers la gauche dans le graphe.

                          Donc, un langage générant du C peut être plus rapide et concis qu'un code C fait main.

                          "La première sécurité est la liberté"

                  • [^] # Re: un inconvénient des templates

                    Posté par . Évalué à 1. Dernière modification le 25/04/13 à 23:29.

                    Haskell vu sa gestion de la mémoire à forcément des cas pathologiques.

                    C’est quoi le problème de la gestion de la mémoire en Haskell ?

                    J’en voit plusieurs, mais ils ont tous des solutions, et c’est pour ça que j’utilise pas mal Haskell pour faire du calcul multi-dimensionnel sur des données volumineuses. Tu veux peut-être parler du fait que ce soit un langage de haut-niveau avec mémoire collectée, et que les paramètres du collecteur influencent les performances ? Ça tombe bien, il existe des outils graphiques pour analyser ça. Tu veux peut-être dire que l’évaluation paresseuse (en pensant thunks) peut remplir la pile ? Ça tombe bien, les bang patterns facilitent grandement l’écriture de code strict. Tu veux peut-être aussi parler du fait que tout les types sont par défaut boxés et donc prennent donc plus de place ? Ça tombe encore pas mal, vu qu’il existe pas mal de bibliothèques de type Array qui peuvent unboxer les types (e.g. repa, vector), et on peut aussi le faire directement à la main.

                    Évidemment, ça suppose de pas mal d’avoir un poil de connaissance sur le langage (Haskell restant un gros langage avec pas mal de concepts fonctionnels à connaître), ses extensions, son implémentation, et ses bibliothèques. Mais c’est pareil pour avoir des trucs optimisés en C. L’avantage est d’avoir un code relativement sûr, concis, avec pas mal d’optimisations faites par le compilateur en collaboration avec les bibliothèques (fusion de boucles, parallélisation automatique, utilisation des GPUs).

                    • [^] # Re: un inconvénient des templates

                      Posté par . Évalué à 4.

                      j’utilise pas mal Haskell pour faire du calcul multi-dimensionnel sur des données volumineuses

                      Le truc qui me fait peur avec un langage qui gère automatiquement la mémoire c'est la tendance à créer des intermédiaires pour les calculs sans qu'on puisse l'empêcher. Et ça peut vraiment faire la différence en termes de performances, en tout cas c'est ce que j'observe entre faire du python/numpy et du C++.

                      Donc je me demandais si haskell permettait d'avoir le contrôle là dessus si désiré?

                      • [^] # Re: un inconvénient des templates

                        Posté par . Évalué à 0.

                        Le problème est assez complexe, avec Ocaml tu peux facilement prédire les données que tu va créé. Haskell dispose d'optimisation dite de "deforestation" qui supprime les arbres de données intermédiaires, quand il le peut. Ocaml n'étant pas purement fonctionnel, j'imagine que la difficulté vient de là.

                        "La première sécurité est la liberté"

                      • [^] # Re: un inconvénient des templates

                        Posté par . Évalué à 5.

                        Puisque tu parles de NumPy, j’imagine que tu veux parler du problème soulevé par son auteur. En clair, si tu écris une expression du genre c = a**2 + exp(b) avec a,b :: numpy.array, l’interpréteur va d’abord créer un objet pour a**2, puis un pour exp(b) et enfin sommer ces objets intermédiaires. Dans le même post sur son blog, l’auteur nous donne deux solutions qui se basent sur Cython ou Weave, en clair définir l’opération à effectuer dans un langage rapide et l’appeller depuis Python. On pourrait aussi rajouter la solution numexpr à la liste. Créer le code en C est effectivement la solution standard pour accélérer Python, en particulier pour réutiliser les bibliothèques existantes.

                        Pour illustrer la version Haskell, on peut regarder comment fait la bibliothèque repa. En reprennant la même expression, repa ne va pas créer des tableaux entiers intermédiares, mais seulement un tableau retardé qui indique que pour avoir les éléments du résultat exp(b) (en syntax NumPy), il faudra appliquer exp aux éléments de b. En gros, un tableau retardé c’est une fonction. Quand tu vas combiner a**2 exp(b), tu vas encore une fois créer une nouvelle fonction qui va dire comment trouver les éléments de c (et qui en interne va utiliser a et b). En gros le vrai tableau n’est généré (de façon parallèle !) qu’au moment où tu en as besoin (en le forçant), en utilisant différentes astuces pour avoir un code (par exemple, si tu fais f(exp(b)), il va automatiquement combiner f et exp pour avoir un code optimal). Ça marche aussi de façon unifiée pour les changements de coordonnées (ça NumPy le fait un peu).

                    • [^] # Re: un inconvénient des templates

                      Posté par . Évalué à 1.

                      Je pensais plus au fait qu'il était trés difficile de déterminer la taille des données mémoires. Certain industriel avait laisser tomber Haskell car de petites modifications pouvait faire exploser son usage de la mémoire. C'était un des arguments d'OCaml ou sa gestion des objets mémoire est très prévisible.

                      "La première sécurité est la liberté"

                • [^] # Re: un inconvénient des templates

                  Posté par . Évalué à 5.

                  Ça me fait penser qu'on m'avais vanté la vitesse de java sur le C avec code à l'appui. J'ai vite regardé le code, parcours à l'envers d'un tableau, pas d'option de compil genre -O3 ou -O2, bref du beau n'importe quoi.

                  Il ne faut pas décorner les boeufs avant d'avoir semé le vent

                  • [^] # Re: un inconvénient des templates

                    Posté par . Évalué à 1.

                    J'ai écrit et optimier une IA en java. La vitesse est digne d'un gcc -O0, pas d'inline des getter/setter, etc… Cela ne peut pas être rapide, même les "clause if" n'étaient pas sorti des boucles.

                    "La première sécurité est la liberté"

                  • [^] # Re: un inconvénient des templates

                    Posté par . Évalué à 1.

                    J'ai aussi vu ce bench, je crois :)
                    C'était un moment d'anthologie pour moi qui aime rire.

              • [^] # Re: un inconvénient des templates

                Posté par . Évalué à 1.

                C'est marrant de dire qu'un langage mort est capable de faire mieux que les existants… pourtant, il y a pas mal de langages de niche (dans la catégorie merdique, j'appelle PowerBuilder!), donc je suis intrigué qu'il n'ait pas réussi s'il est meilleur (bien que ce soit très possible, je ne le nie pas).

                Lisaac a été premier de ce benchmark

                Ce benchmark est à 2 dimensions: taille du code (et pas expressivité) à l'horizontale et vitesse d'exécution sur la hauteur, si je ne me trompe pas?
                Pour le coup (prêcher pour ma paroisse avec force mauvaise foi - comme ça c'est dit ;) - ne m'a jamais empêché de dormir) je remarque que comparer ocaml au C++ est intéressant en effet j'ai regardé sur du quad-core (C++ et les thread c'était pas ça, jusqu'au C++ après tout… toujours pas testé d'ailleurs) et je remarque qu'Ocaml est plus économe en mémoire. Sur 3 domaines. Equivalent sur 2, et il perds sur 5.

                Niveau vitesse, il atteint C++ sur 2 domaines, les autres C++ gagne.
                Code plus petit (de moitié) dans 1 cas, plus grand dans 2 cas, et équivalent dans les autres.

                Pour le coup, je te remercie, ce bench est très intéressant, même si je m'en suis un peu servi pour embêter le monde mesquinement :)

                Notes un peu plus objectives et de moins mauvaise foi:

                • dans la config ou OCaml se place le mieux, c'est à dire x86 un seul coeur, il se fait moins démonter… Problème de compilateur en retard dans les autres domaines?
                • je n'ai pas utilisé les graphiques pour comparer, ni les chiffres brut, mais la section " 2 : Are the OCaml programs faster? Approximately.". C'est celle qui m'arrange le plus après tout ;)
                • je n'ai pas pu regarder la comparaison avec "shortest C++", je sais pas pourquoi. Dommage, ça m'aurait pas mal intéressé… rien que comparer shortest C++ avec G++C++ pour voir à quel point l'optimisation est utile aurait été sympa
                • il n'y a que des algo de calcul, donc pas d'accès aux ressources, de gestion d'IHM, et caetera. Donc, comme d'hab avec les bench, résultats à n'utiliser que pour des optimisations des "zones de calcul".
                • ils mesurent la taille du code en octets, en ayant enlevé commentaires et espaces, et en compressant le texte ainsi obtenu. Franchement… je trouve difficile de trouver une façon moins pertinente de mesurer la taille des codes.
                • [^] # Re: un inconvénient des templates

                  Posté par . Évalué à 2. Dernière modification le 25/04/13 à 16:11.

                  "je suis intrigué qu'il n'ait pas réussi s'il est meilleur"

                  Le code du compilo n'était pas simple du tout (détermination du type réel de chaque objet). Et le dev principal a coupé les ponts du jour au lendemain.

                  Les codes c++ et C utilisent massivement openMP qui n'existe pas dans le monde ocaml.

                  "Ce benchmark est à 2 dimensions: taille du code (et pas expressivité) à l'horizontale et vitesse d'exécution sur la hauteur, si je ne me trompe pas?"

                  Non, c'est juste l'affichage de ce graph, il est intéressant car la case la plus intéressante est en bas à droite : compact et rapide.

                  "ils mesurent la taille du code en octets, en ayant enlevé commentaires et espaces, et en compressant le texte ainsi obtenu. Franchement… je trouve difficile de trouver une façon moins pertinente de mesurer la taille des codes."

                  Au contraire, cela évite de parler de la définition de la "ligne de code". La compression limite l'augmentation de taille des langage verbeux par rapport à ceux abusant des symboles.

                  "il n'y a que des algo de calcul, donc pas d'accès aux ressources, de gestion d'IHM, et caetera. Donc, comme d'hab avec les bench, résultats à n'utiliser que pour des optimisations des "zones de calcul"."

                  C'est vrai. Mais les problèmes de logiciels dans ce domaine, sont liés aux algos utilisés. Il faut aussi que la taille du benchmark reste accessible aux codeurs. Il y a aussi des benchs manipulant des symboles (de mémoire, le truc qui manipule de l'ADN).

                  "La première sécurité est la liberté"

                  • [^] # Re: un inconvénient des templates

                    Posté par . Évalué à -1.

                    Les codes c++ et C utilisent massivement openMP qui n'existe pas dans le monde ocaml.

                    Je connais assez mal openMP, mais qu'est-ce qui empêche OCaml de l'utiliser? Pas moyen d'utiliser une API C dans OCaml? Mais alors, comment faire pour utiliser l'API de l'OS sous-jacent?

                    Désolé, mais je trouve cet argument plutôt contre que pour OCaml. Même Java et C# savent exploiter une API C… Ce serait dû au fait de ne pas être impératif?

                    Non, c'est juste l'affichage de ce graph, il est intéressant car la case la plus intéressante est en bas à droite : compact et rapide.

                    From more-concise at page left to less-concise at the right, from slower at page top to faster at the bottom.

                    Les langages les plus rapides et concis sont en bas à gauche, non? J'ai toujours du mal avec droite et gauche en anglais…

                    Au contraire, cela évite de parler de la définition de la "ligne de code". La compression limite l'augmentation de taille des langage verbeux par rapport à ceux abusant des symboles.

                    Argument intéressant.
                    Mais je voulais surtout dire que je ne considère pas la taille d'un code comme un indice par rapport au fait qu'il soit lisible: perl produit des codes pouvant parser des documents complexes en peu de lignes, mais il est tout bonnement horrible à lire… A contrario, java est très simple à lire, mais j'ai l'impression qu'il se transforme souvent en romans à l'eau de rose, et mieux vaut avoir 2 écrans larges pour coder confortablement.

                    Mais les problèmes de logiciels dans ce domaine,

                    De quel domaine parles-tu? Dans la discussion, on parlait de façon assez générale il me semble… cette série de bench est intéressante, je suis d'accord, je rappelais juste (pour la même raison que je parlais de mauvaise foi en défendant le C++ sur tous les points) que les bench sont toujours là pour montrer l'efficacité dans un domaine précis.
                    Ici, ça semble être le calcul scientifique?

                    • [^] # Re: un inconvénient des templates

                      Posté par . Évalué à 3.

                      openMP n'est pas une api C. Ce sont des directives de compilation pour générer du code multithread.

                      "la taille d'un code comme un indice par rapport au fait qu'il soit lisible"

                      Non, mais sur sa concision. Il y a eu des études portant sur la productivité (cocomo) quelque soit le langage, la productivité en ligne de code par unité de temps est la même.

                      "De quel domaine parles-tu? Dans la discussion, on parlait de façon assez générale il me semble…"

                      Je pensais à l'IHM.

                      "Ici, ça semble être le calcul scientifique?"

                      Non, il y a aussi manipulation de symbole.

                      "La première sécurité est la liberté"

                      • [^] # Re: un inconvénient des templates

                        Posté par . Évalué à 1.

                        openMP n'est pas une api C. Ce sont des directives de compilation pour générer du code multithread.

                        Et on peut pas faire la même chose avec OCaml? (pour la confusion: l'encart rapide sur wikipedia UK dit "Written in C, C++, Fortran" et "Type API" mais je te crois sur parole: n'ayant jamais utilisé et ayant déjà relevé des erreurs ou imprécisions sur wikipedia, je préfère faire confiance à quelqu'un tant que ses propos sont pas aberrants :) )

                        Il y a eu des études portant sur la productivité (cocomo) quelque soit le langage,

                        J'ai entendu parle de cocomo… quand j'ai parlé du problème du sucre syntaxique plus important des langages objet genre C++ (public/protected/private sur une ligne propre, accolades souvent seuls sur leurs lignes, déclarations/définitions dans des fichiers distincts -donc 2 lignes de code par prototype-, …) il m'a répondu que c'était plutôt utilisé par les commerciaux et a clairement remis en cause l'intérêt. J'ai gardé le nom dans un coin de mémoire morte mais sans creuser plus du coups.
                        Si tu as des ressources intéressantes à ce sujet je suis preneur, si tu estimes que ça permets vraiment d'estimer les coûts d'un projet en fonction des langages utilisés.

                        • [^] # Re: un inconvénient des templates

                          Posté par . Évalué à 3.

                          #pragma omp for
                           for(int n=0; n<10; ++n)
                           {
                             printf(" %d", n);
                           }
                           printf(".\n");
                          
                          

                          Cela va te lancer n thread en découpant la clause for en morceau. Le système se débrouille même si tu peux aussi le guider. Sur du code numérique, c'est assez facile du moment que tu comprends que tu ne peux pas partager d'information entre threads.

                          Si tu as des ressources intéressantes à ce sujet je suis preneur, si tu estimes que ça permets vraiment d'estimer les coûts d'un projet en fonction des langages utilisés.

                          https://en.wikipedia.org/wiki/COCOMO ?

                          Je retient surtout : ab*(KLOC)bb. Cela veut dire que la difficulté n'est pas linéaire au nombre de ligne, mais augmente plus vite.

                          "La première sécurité est la liberté"

                          • [^] # Commentaire supprimé

                            Posté par . Évalué à 4.

                            Ce commentaire a été supprimé par l'équipe de modération.

                            • [^] # Re: un inconvénient des templates

                              Posté par . Évalué à 3.

                              cocomo ne mesure pas la difficulté de changement, mais le temps global pour écrire le code. Le problème est que le nombre exacte de kloc est connu à la fin du projet.

                              Et si le cout est 100x, c'est parce qu'au début, cela prend 5min pour changer une spec, alors qu'à la fin, c'est le client qui trouve l'erreur, la remonte au support, qui transmet à la validation pour confirmation, qui remonte ensuite au dev.

                              "La première sécurité est la liberté"

                        • [^] # Re: un inconvénient des templates

                          Posté par (page perso) . Évalué à 2.

                          Ocaml est plombé par la gestion des threads. Il n'est pas possible de faire de code multithreadé en ocaml, à cause du garbage collector.

                          Des tentatives pour compiler une version compatible ont été tentées (oc4mc), mais n'ont jamais été intégrées dans la branche principale, et ne le seront jamais (des messages sur la ml que je ne retrouve plus en écrivant ce post disent en gros que des solutions pour faire du calcul distribué existent et fonctionnent bien, gérer plusieurs processus nativement n'est donc pas à l'ordre du jour).

                          C'est un peu le problème qu'avait rencontré python il y a quelques années, mais avait réussi à s'en sortir.

                          En dehors de ça, j'ai l'impression que le langage Ocaml recommence à faire parler de lui, après quelques années en sommeil. Un regain pour le fonctionnel ?

                          • [^] # Re: un inconvénient des templates

                            Posté par . Évalué à 1.

                            Disons que faire du pthread propre est une vrai horreur. Le vrai multi-process est revenu à l'honneur avec chrome. Il "suffit" d'ajouter une lib de passage de message propre et rapide.

                            Mais c'est vrai aussi que des traitements parallèle de data pourrait être sympa (map fold/reduce en multicpu).

                            "En dehors de ça, j'ai l'impression que le langage Ocaml recommence à faire parler de lui, après quelques années en sommeil. Un regain pour le fonctionnel ?"

                            Peut être est-ce la création de Ocaml pro ? Ou la pub sur l'usage qu'en font quelques boite comme janes street (vive le HFT en Ocaml).

                            "La première sécurité est la liberté"

                            • [^] # Re: un inconvénient des templates

                              Posté par (page perso) . Évalué à 2.

                              Mais c'est vrai aussi que des traitements parallèle de data pourrait être sympa (map fold/reduce en multicpu).

                              Il y a parmap qui fait ça (mais avec des bindings C, on n'est plus dans Ocaml pur).

                              Peut être est-ce la création de Ocaml pro ?

                              En fait, j'ai l'impression que le langage commence à monter son propre environnement : Gestionnaire de paquets, librairies avancées, ce qui manquait pour sortir d'un simple langage théorique. Il reste encore beaucoup à faire (compiler sous windows reste encore une épreuve), mais ça commence à bouger. Je ne connais pas assez Ocaml pro pour savoir s'ils accompagnent le mouvement ou s'ils donnent une nouvelle dynamique.

                              • [^] # Re: un inconvénient des templates

                                Posté par . Évalué à 1.

                                Disons que le dev d'ocaml était purement universitaire, ils étaient plus intéressé dans le développement des derniers concepts avancés de typage (le dernier type à l'air super puissant, mais je n'ai toujours pas compris son fonctionnement) que dans le fait d'avoir des messages d'erreurs précis et compréhensible, ou un module Eclipse fonctionnel. Ocaml pro veut changer cela, on dirait.

                                "La première sécurité est la liberté"

                • [^] # Re: un inconvénient des templates

                  Posté par (page perso) . Évalué à 5.

                  ils mesurent la taille du code en octets, en ayant enlevé commentaires et espaces, et en compressant le texte ainsi obtenu. Franchement… je trouve difficile de trouver une façon moins pertinente de mesurer la taille des codes.

                  Au contraire, la taille du fichier compressé est une bonne approximation de la quantité d'information qu'il contient.

      • [^] # Re: un inconvénient des templates

        Posté par (page perso) . Évalué à 2.

        Il faut utiliser un compilateur récent qui donne des erreurs correcte. Comme clang. Même GCC 4.8 a fait beaucoup de progrès à ce niveau.
        Et puis les bons IDE te présentent les erreurs correctement.

      • [^] # Re: un inconvénient des templates

        Posté par . Évalué à 4.

        Ça fait quelques années que j'ai tendance à utiliser systématiquement des langages de haut niveau, et quand je vois des trucs comme ça,

        Allez, j'ai envie de tomber dans ton troll:

        Ca tombe bien, C++ est un langage de haut niveau. Il est aussi bas niveau, cependant. Donc moyen niveau selon wikipedia, parce qu'il intègre des éléments des 2.
        Note quand même que, perso, je me passe très bien de l'allocation mémoire, y compris pour les collections polymorphes, grâces à boost::ptr_container.

        C++

        C++ (pronounced "see plus plus") is a statically typed, free-form, multi-paradigm, compiled, general-purpose programming language. It is regarded as an intermediate-level language, as it comprises both high-level and low-level language features.

        quand je vois des trucs comme ça

        Le truc que tu vois qui ne te donne pas envie, ça s'appelle le paradigme de la programmation générique et selon wikipedia, "C'est un concept important pour un langage de haut niveau car il permet d'augmenter le niveau d'abstraction du langage."

        Faut pas confondre langage de haut niveau et langage simpliste :D

        plus imbittable que le code C++: les erreurs retournées par le compilateur, avec un petit bonus quand la STL est impliquée et qu'il faut scroller dans le terminal pour retrouver le début de la dernière erreur…

        Enfin un peu de vérité… un poil obsolète, mais malgré tout encore d'actualité. Essaies clang.

        Pour clôturer ce message, le problème du C++ c'est pas le langage lui-même, mais la pauvreté de sa lib standard, qui a grandement été fixé par C++11 et devrait être encore largement amélioré dans 2-3 ans. En attendant, y'a boost.
        Et pour les IHM, t'as le choix: MFC, Qt, GTK, WxWidgets…

  • # C++ et les templates de haut vol

    Posté par (page perso) . Évalué à 3.

    Comme dit Nicolas Boulay, ça demande de la réflexion pour comprendre. Ce qui me rend perplexe, c'est quand on pose le bouchon plus loin pour faire des trucs tordus du style méta-programming qu'on trouve dans boost. Pour moi, c'est tellement complexe que ça en devient une boîte noire. Un truc avec une syntaxe proche du c++, mais qui crachera des messages d'erreur incompréhensibles en cas de typo.
    Et quand c'est compliqué, trop éloigné de l'usage de base du langage, ça devient difficile de faire du support, surtout si il s'agit du code d'un autre (par exemple un collègue parti pour d'autres horizons).

    • [^] # Re: C++ et les templates de haut vol

      Posté par (page perso) . Évalué à 6.

      Une fois habitué au style "boost", ce n'est pas si difficile, mais par contre les temps de compilation tendent vers l'infini et au delà.

      http://devnewton.bci.im

    • [^] # Re: C++ et les templates de haut vol

      Posté par . Évalué à 4.

      C++11 à justement intégré des outils qui permettent de simplifier les messages d'erreur: les assertions statiques

      Sinon, je ne crois pas que le C++ ait un jour été destiné à être un langage de programmation pour débutant.
      Pas plus que les design pattern ne doivent être employés sans réflexion (surtout pas même, ça te pourris un code très vite sinon), donc la réflexion qu'engage cet article me semble plutôt salutaire.
      De là à qualifier les sources affichées de "template de haut vol" par contre, il y a un gouffre que je n'oserais pas franchir. Si toi oui, alors ne lis jamais le source de la STL fournie avec Gnu… (ça m'arrive régulièrement, mais je suis toujours largué quand je le fais)

  • # Intérêt du visiteur ?

    Posté par . Évalué à 3.

    Salut,
    En ayant lu l’ensemble, je me demande toujours ce qu’est concrètement un visiteur ?
    J’ai l’impression que ça sert à implémenter une sorte de « model-view » ?

    Quelqu’un aurait-il la gentillesse de faire un exemple réel du design-patern ? Car en dehors du bonheur d’apprendre du c++, je veux bien apprendre aussi des design-patern…

    • [^] # Re: Intérêt du visiteur ?

      Posté par (page perso) . Évalué à 5.

      En ayant lu l’ensemble, je me demande toujours ce qu’est concrètement un visiteur ?

      Par exemple Base est abstraite pure et représente les expressions algébriques,les classes dérivées concrètes représentent les atomes (nombres et indeterminées) et les combinateurs + et * (pour simplifier).

      Tu peux alors implémenter les algorithmes développer, dériver ou évaluer comme des visiteurs concrets de la classe de base.

      Le point clef du modèle est que tu veux implémenter un traitement par une classe (le visiteur) qui reçoit un pointeur sur une classe abstraite, surlaquelle le visiteur ne sait rien faire: les traitements ne sont définis que sur les classes concrètes. En renversant l'ordre d'appel (c'est la classe abstraite qui se présente au visiteur par une méthode virtuelle, ce qui permet de récupérer le type concret de la classe).

  • # Polymorphisme

    Posté par (page perso) . Évalué à 5. Dernière modification le 25/04/13 à 01:12.

    En C++ il y a deux mécanismes de polymorphisme: les méthodes virtuelles où la résolution se passe à l'éxécution du programme, et les templates, où la résolution se passe à la compilation.

    Du coup quand on lit un truc du style «Le Visiteur template à base de RTTI» qui annonce mélanger du polymorphisme à l'éxécution et du polymorphisme à la compilation, on se dit à quoi bon?

    on peut passer par un membre mais c'est moins joli.

    Si tu ne veux pas montrer ton membre dans l'espace public, il suffit d'encapsuler l'appel à Visitor::visit dans une fonction qui renvoie la valeur du membre. Au passage, cela permet de donner un vrai nom à la fonction au lieu de visit.

    L'implémentation classique du modèle n'est d'ailleurs pas ce que tu donnes: dans Base la méthode accept est virtuelle et abstraite et implémentée dans les classes concrètes Derived1, Derived2. Dans ce cas Visitor::visit(Base& b) est b.accept(*this).

    • [^] # Re: Polymorphisme

      Posté par . Évalué à 3.

      Si tu ne veux pas montrer ton membre dans l'espace public, il suffit d'encapsuler l'appel à Visitor::visit dans une fonction qui renvoie la valeur du membre. Au passage, cela permet de donner un vrai nom à la fonction au lieu de visit.

      visit n'est jamais appelé directement de l'extérieur (il l'est uniquement dans les fonctions accept), c'est bien accept qui est appelé mais soit, on peut reprendre ton argument et l'adapter à accept. C'est juste une question de style et de simplicité et de choix.

      L'implémentation classique du modèle n'est d'ailleurs pas ce que tu donnes: dans Base la méthode accept est virtuelle et abstraite et implémentée dans les classes concrètes Derived1, Derived2. Dans ce cas Visitor::visit(Base& b) est b.accept(*this).

      Alors ça, ça dépend de ce qu'est Base et de ce qu'est Derived1 et Derived2. Et si on applique le petit plus que j'évoque (appeler le visiteur de la classe mère), tu auras un accept pour Base.

    • [^] # Re: Polymorphisme

      Posté par . Évalué à 10.

      Si tu ne veux pas montrer ton membre dans l'espace public […]

      C'est surtout que tu va au devant de problème avec la police.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # Jour... Nuit...

    Posté par . Évalué à -3.

    Okay !

    Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

Suivre le flux des commentaires

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