Je crée mon jeu vidéo E09 : Techniques de C++11 appliquées au système à entités

Posté par  (Mastodon) . Édité par palm123 et Benoît Sibaud. Modéré par ZeroHeure. Licence CC By‑SA.
Étiquettes :
38
12
fév.
2014
Jeu

«Je crée mon jeu vidéo» est une série d'articles sur la création d'un jeu vidéo, depuis la feuille blanche jusqu'au résultat final. On y parlera de tout : de la technique, du contenu, de la joie de voir bouger des sprites, de la lassitude du développement solitaire, etc. Vous pourrez suivre cette série grâce au tag gamedev.

Dans le dernier épisode, on a parlé du livre de Jesse Schell, «L'Art du Game Design». Mais bon, comme il paraît qu'on ne discute pas assez de technique sur LinuxFR, donc cette fois, on va causer uniquement de technique. Du gros, du lourd, du C++ ! Et dans sa version 2011 pour que ça soit encore plus imbitable pour le commun des mortels. Comme ça, on discutera entre nous, techniciens divins et on laissera la bonne plèbe se vautrer dans la mélasse. (Est-il nécessaire que je rajoute une balise humour ?)

Sommaire

C++11 mon amour

Programmer un jeu en C++11 (connu auparavant comme C++0x) est l'occasion de tester des fonctionnalités de cette nouvelle norme. Et les systèmes à entités donnent l'occasion de faire des choses assez élégantes. Comme Bjarne Stroustrup, inventeur du C++, je ne suis pas loin de penser que C++11 est presque un nouveau langage tellement tout paraît plus facile et naturel. Une fois essayé, difficile de s'en passer. Voici donc deux exemples qui utilisent des fonctionnalités C++11 et qui sont intégrés à libes et utilisé dans mon jeu.

Des gestionnaires d'événements vraiment génériques

Dans le patron de conception Observateur qui est à la base de certains systèmes d'événements, l'observateur est censé hériter d'une classe Observer et implémenter une fonction notify() qui sera appelée au moment adéquat. En C++, cela se traduit souvent par une classe de base avec une fonction virtuelle pure :

class Observer {
public:
  virtual void notify() = 0;
}

Cette technique n'est pas très pratique, surtout comparé à Java où on aurait une simple interface (et non une classe, même abstraite). Heureusement, C++11 vient avec un moyen encore plus puissant que Java : std::function !

std::function, c'est les pointeurs de fonction en démultiplié ! Bon d'accord, la comparaison n'est peut-être pas idéale mais disons que c'est l'idée. Les amateurs de langages fonctionnels trouveront sûrement que cette fonctionnalité est triviale, et implémentée de manière verbeuse et inélégante, mais en C++, c'est nouveau et c'est révolutionnaire !

Concrètement, comment peut-on s'en servir ? Et bien je vais prendre l'exemple de libes et de ses gestionnaires d'événements. Dans libes, un gestionnaire d'événement est défini de la manière suivante :

typedef std::function<EventStatus(Entity, EventType, Event*)> EventHandler;

Ce qui veut dire qu'un EventHandler est une «fonction» prenant en paramètre une entité (l'origine de l'événement), un type d'événement et un pointeur vers les données de cet événement et renvoyant un statut (que je ne détaille pas ici). Ça a l'air limité mais en fait, ça ne l'est pas, au contraire. Parce qu'on peut avoir une vraie fonction :

EventStatus monGestionnaire(Entity e, EventType t, Event *ev) {
  return EventStatus::KEEP;
}

Mais on peut aussi avoir un lambda :

auto monGestionnaireLambda = [](Entity e, EventType t, Event *ev) {
  return EventStatus::KEEP;
}

Mais on peut aussi avoir une méthode d'une classe ! Et c'est là que ça déchire :

class Foo {
  EventStatus maMethodeGestionnaire(Entity e, EventType t, Event *ev) {
    return EventStatus::KEEP;
  }
}

Et dans ces cas-là, on peut l'associer à un objet en particulier via std::bind :

using namespace std::placeholders;

Foo foo;
auto gestionnaire = std::bind(&Foo::maMethodeGestionnaire, &foo, _1, _2, _3)

Ce qui signifie que, quand on appellera gestionnaire avec les trois arguments qui vont bien, en fait, on appellera la méthode maMethodeGestionnaire sur l'objet foo avec les trois arguments. On pourrait faire des choses encore plus drôles en ayant des méthodes qui ont les paramètres dans le désordre, ou dans laquelle il manque des paramètres. Bref, tout est possible avec std::bind !

Maintenant, on n'est donc plus limité par une classe de base, on peut avoir tout ce qu'on veut comme gestionnaire d'événements.

Cerise sur le gâteau, comme ce dernier cas est plutôt courant, libes permet de spécifier un pointeur sur une méthode et un objet et fait le bind automatiquement. Magique !

Comment avoir des identifiants uniques en C++ ?

Dans mon implémentation de libes, j'utilise des identifiants uniques pour les composants et les événements. Ces identifiants doivent être tous différents et différents de zéro (qui est l'identifiant qui représente le composant ou l'événement invalide). Évidemment, cette manière de faire est très utile pour le développeur de libes (moi) qui a un joli entier qu'il peut utiliser pour plein de choses (essentiellement ranger les composants/événements dans une table de hachage) mais pas très pratique pour l'utilisateur de libes.

Et bien désormais, libes permet de ne pas avoir à trop se préoccuper de cet entier (notamment savoir s'il est différent des autres) grâce à la magie de C++. Pour cela, j'ai introduit un littéral définis par l'utilisateur qui, à partir d'une chaîne de caractère, permet d'avoir un entier. En fait, l'entier est obtenu à partir d'un hash de la chaîne, ce qui garantit (presque) que pour deux chaînes différentes, on aura deux identifiants différents (ou alors, c'est vraiment pas de bol !).

Déjà, quel hash utiliser ? Ici, une fonction de hachage non-cryptographique et simple convient. Et même, si elle peut être suffisamment simple pour pouvoir être calculée à la compilation, ce serait parfait. À la compilation ? Oui, C++ permet, grace au mot-clef constexpr de calculer des choses à la compilation. Donc, notre fonction de hachage doit être constexpr, ce qui implique qu'elle doit tenir sur une seule ligne ! Heureusement, tout un tas de fonction de hachage sont comme ça.

J'ai donc choisi une variante d'un hash FNV. Voilà son implémentation en C++ :

constexpr uint64_t Hash(const char *str, std::size_t sz) {
  return sz == 0 ? 0xcbf29ce484222325 : (str[0] ^ Hash(str + 1, sz - 1)) * 0x100000001b3;
}

On peut voir que la variante vient du fait qu'ici, à cause de l'appel récursif, on prend les données à l'envers, c'est-à-dire qu'on commence par la fin et on remonte jusqu'au début. Ce n'est pas très grave, ça donne les mêmes résultats en terme de collisions potentielles.

Ensuite, il n'y a plus qu'à définir un nouveau littéral. À noter que les littéraux définis pas les utilisateurs doivent commencer par _, les littéraux commençant par une lettre étant réservés pour un usage futur (comme en C++14 où on aura plusieurs littéraux de ce genre dans la bibliothèque standard). Ici, on choisit _type :

constexpr uint64_t operator"" _type(const char *str, std::size_t sz) {
  return Hash(str, sz);
}

Maintenant, pour définir un identifiant d'un composant (par exemple), on peut faire :

struct Foo {
  static const es::ComponentType type = "Foo"_type;
}

Et cette constante est calculée à la compilation, pas à l'exécution. On a l'avantage d'avoir un identifiant clair sous forme de chaîne de caractères et un identifiant entier pour le développeur, sans aucun surcoût à l'exécution, bref que des avantages.

Après, on pourrait s'amuser à définir des macros pour encapsuler tout ça, ou le générer automatiquement (ce que je fais dans mon jeu), mais tout ça est laissé à l'utilisateur, la bibliothèque ne fournit que le mécanisme de base et c'est déjà pas mal.

Des nouvelles du front

Pas grand chose de nouveau par rapport à la dernière fois. Je continue ma réflexion sur les dialogues et malgré l'excellent lien qui m'a été fourni à propos du jeu Andor's Trail, il y a encore des zones d'ombre que je souhaite éclaircir avant de me lancer dans un début d'implémentation. Mais j'ai vraiment hâte d'attaquer cette partie.

Par ailleurs, j'ai terminé un gros refactoring (nécessaire) sur le chargement de la carte et j'ai commencé à spécifier un peu proprement la manière dont j'allais construire ma carte graĉe à Tiled.

Aller plus loin

  • # Nouveau ou pas

    Posté par  . Évalué à 2.

    J'ai lu avec intérêt ton nouveau message, et en ce qui concerne l'utilisation de std::function et std::bind, j'ai plus l'impression qu'il s'agit d'aides pour simplifier l'écriture de functors.

    J'utilise déjà des functors sur des méthodes de classe depuis 2005 dans une version très proche (puisqu'inspirée) du livre "Modern C++ Design" de Andrei Alexandrescu.
    Qui est d'ailleurs un excellent livre que je conseille à tous ceux qui veulent s'initier à la programmation générique en C++.

    Pour ce qui est des littéraux, je ne m'étais jamais penché sur cette spec, et je trouve ça sympa, mais mais je me demande quelle est la différence entre cela et utiliser une fonction normale.
    J'ai peut-être raté un truc mais j'ai l'impression que ça n'apporte qu'une nouvelle syntaxe sur des fonctionnalités déjà existantes.

    • [^] # Re: Nouveau ou pas

      Posté par  (Mastodon) . Évalué à 3.

      J'ai lu avec intérêt ton nouveau message, et en ce qui concerne l'utilisation de std::function et std::bind, j'ai plus l'impression qu'il s'agit d'aides pour simplifier l'écriture de functors.

      Tu n'as pas tout à fait tort. Effectivement, ça existe depuis un moment, notamment dans boost, mais maintenant, c'est en standard. Et en plus, ça se couple très bien avec les lambdas, et ça, ça n'existait pas avant. Ça permet de s'éviter du code superflu quand on doit faire une petite fonction et qu'on n'a pas envie de déclarer une fonction à part, ou une classe.

      je me demande quelle est la différence entre cela et utiliser une fonction normale

      Tout se fait à la compilation plutôt qu'à l'exécution. Ça améliore les performances et ça peut améliorer la lisibilité. Par exemple dans C++14, ils vont introduire les suffixes "h", "min", "s", "ms", "us", "ns" pour créer automatiquement des durées (std::chrono::duration). Ça va permettre d'écrire des choses du genre :

      auto total = 1s + 12min + 3h;
      • [^] # Re: Nouveau ou pas

        Posté par  . Évalué à 4.

        Tout se fait à la compilation plutôt qu'à l'exécution.

        C'est le constexpr qui permet cela, tu peux sans soucis écrire :

        auto total = s(1) + min(12) + h(3);

        après je suis d'accord que cela ajoute un peu de lisibilité à certains codes, et aussi cela à l'avantage de proposer un namespace vide, donc pas de risque de collision avec un suffixe "h" qui est déjà défini par l'utilisateur.

        Mais ce qui est nouveau dans ton cas d'utilisation, c'est le constexpr qui permet l'évaluation à la compilation.

        • [^] # Re: Nouveau ou pas

          Posté par  (Mastodon) . Évalué à 2.

          C'est le constexpr qui permet cela

          Oui, c'est vrai, c'est plutôt le constexpr qui permet ça. Mais même dans ton exemple, tu pourrais avoir des constructeurs constexpr.

          donc pas de risque de collision avec un suffixe "h" qui est déjà défini par l'utilisateur.

          En fait, dans C++14, il y aura deux suffixes "s" : un pour les secondes et un pour les chaînes. Mais normalement, aucune collision vu que celui pour les secondes s'appliquent à des nombres et celui pour les chaînes s'appliquent… à des chaînes.

          • [^] # "constexpr" oui, "string literals" non

            Posté par  . Évalué à 1.

            Je suis très intéressé par cette nouvelle dépêche, de cette série que je suis depuis le début ; ce weekend j'ai justement entrepris de récupérer, de comprendre et d'expérimenter l'utilisation de ta libes !

            Une première remarque :

            Maintenant, pour définir un identifiant d'un composant (par exemple), on peut faire :

            struct Foo {
                static const es::ComponentType type = "Foo"_type;
            }

            De mon point de vue, cette syntaxe est assez obscure, tout au moins dans ce cas d'usage (contrairement à l'exemple de "3min + 5s"). J'aurais tendance à privilégier une écriture de la forme suivante, exprimant plus lisiblement l'intention :

            struct Position : public Component {
                static const es::ComponentType type = CompileTimeHash("Position");
            }

            Finalement, dans mes expérimentations avec libes j'ai préféré utiliser de simples entiers saisis manuellement !

            struct Position : public Component {
                static const es::ComponentType type = 1;
            }
            struct Speed : public Component {
                static const es::ComponentType type = 2;
            }
            ...
            • [^] # Re: "constexpr" oui, "string literals" non

              Posté par  (Mastodon) . Évalué à 2.

              J'aurais tendance à privilégier une écriture de la forme suivante, exprimant plus lisiblement l'intention

              Là, je pense que c'est une question de goût. Personnellement, je trouve la version avec le user-defined literal plus simple et lisible. Tu peux utiliser la fonction Hash qui aura le même effet.

              Finalement, dans mes expérimentations avec libes j'ai préféré utiliser de simples entiers saisis manuellement !

              C'est possible aussi ;)

              D'ailleurs, c'est toi qui a envoyé un pull request, je ne me trompe pas ? Si tu as d'autres remarques à faire à propos du code, que tu as besoin de fonctionnalités, n'hésite pas à demander. Pour l'instant, je développe au fur et à mesure de ce que j'ai besoin et donc, ça change beaucoup (souvent par ajout). Mais si tu as d'autres besoins, ça m'intéresse de les intégrer.

              • [^] # Re: "constexpr" oui, "string literals" non

                Posté par  . Évalué à 1.

                Effectivement, j'aime bien proposer quelques retouches mineures lorsque je lis du code qui m'intéresse et/ou que je souhaite utiliser.

                Souvent mes contributions concernent la portabilité ou les outils, en me basant sur mon environnement de développement et mes quelques expérimentations personnelles.
                Typiquement :
                - Je développe avec GCC 4.7.2 et Visual Studio 2010 (là pour libes c'est mort) et Visual Studio 2013 (j'ai des patchs à proposer, et les constexpr ne sont supportés qu'avec le dernier CTP d'Octobre !)
                - J'utilise Travis-CI pour faire de l'intégration continue avec GCC 4.6 (là aussi pour libes c'est mort, j'ai essayé) et Clang 3.3

                Par ailleurs, j'aime bien lorsqu'il y a un exemple ultra simple sans dépendances externes, activable en option dans CMake)

                • [^] # Re: "constexpr" oui, "string literals" non

                  Posté par  (Mastodon) . Évalué à 2.

                  Pour le problème des versions de compilateurs qui n'ont pas encore certaines fonctionnalités de C++11, je pense que je vais gérer ça globalement via une option dans CMake. En gros, une option LIBES_FULL_CXX11 qui indique qu'on utilise un compilateur qui implémente tout C++11 (désactivé par défaut). Et pour les autres (typiquement les cas que tu cites), on trouve des alternatives. Dans une autre lib, j'avais déjà eu ce genre de souci quand j'avais essayé de compiler avec le GCC de mingw64 qui doit être un 4.7 si ma mémoire est bonne.

    • [^] # Re: Nouveau ou pas

      Posté par  . Évalué à 3.

      "Modern C++ Design" de Andrei Alexandrescu. Qui est d'ailleurs un excellent livre que je conseille à tous ceux qui veulent s'initier à la programmation générique en C++.

      J'essaie de commencer à programmer en C++ et ce livre a l'air d'une bonne référence, mais j'hésite à acheter aussi ancien (2001). Il démontre peut-être de bonnes idées, mais s'il utilise une implémentation très complexe prévue pour C++98, qui se ferait bien plus simplement en C++11 (comme dans l'exemple donné dans cette dépêche), cela pourrait m'embrouiller. Y a-t-il des choses qui sont encore pertinentes dans ce livre depuis la publication de C++03, C++11 et bientôt C++14 ?

      • [^] # Re: Nouveau ou pas

        Posté par  . Évalué à 2.

        Si tu commences, je ne pense pas que cela soit le meilleur livre pour toi.

        Je le trouve vraiment très bon, il me semble (mais je dis peut-être des bétises, car je l'ai lu en 2004…) que tout ce qu'il contient est compatible C++98, mais pour autant il y expose des techniques très intéressantes (au niveau implémentation de certains design pattern, et aussi certaines techniques algorithmiques) que j'utilise encore aujourd'hui dans mon travail.
        Je le conseillerai à quelqu'un qui connais et a déjà un peu programmé en C++ (au moins un an, avec un petit background en programmation en général, et pourquoi pas avec lu "Design Patterns" avant)

        Je ne connais pas beaucoup de bons livres pour débutant, mais je suis sûr que d'autres sauront te renseigner.

  • # Intéressant

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

    C'est vrai que le C++11 a l'air de proposer des ouvertures intéressantes.

    Pour ce qui est de l'opérateur "" + le litéral _type, j'avoue que je suis un peu effrayé. Est-ce qu'on peut utiliser l'opérateur "" tout court ? Est-ce que ça risque pas de pourrir toutes les chaînes de caractères de ton programme + de toutes les lib qu'il utilise ?

    Par contre, je suis de plus en plus convaincu que le C++ souffre d'un gros problème, qui ne fait que s'empirer avec le C++11 : très peu d'être humains sont capables de comprendre tout le langage C++. Et les programmes sont encore écrits par des êtres humains … donc par des gens qui ne comprennent pas toutes les conséquences ce qu'ils écrivent. Et c'est encore plus vrai pour ceux qui les relisent.

    Les nouvelles additions permettent vraiment de faire des trucs intéressants, et on peut dire que aucun problème n'échappera au C++. Mais à quel prix ? Combien de mot-clés en plus, de constructions bizarres, de symboles pas facile à comprendre ? Je note que pour chacune des nouvelles constructions (template, lambda, …), j'ai de plus en plus de mal à faire le tri entre les valeurs retournées, les valeurs sur lesquelles portent la fonction, nom de la fonction elle-même.

    Je comprends le désir pour le C++ de rester dans la course avec du typage allégé, des closures, des lambda, des template plus light, etc. Mais le langage perd à chaque fois en simplicité et lisibilité.

    De mon point de vue, un langage de programmation doit rester concis et clair. Pas trop de mot-clés, pas trop de constructions alambiquées.

    J'avais été effaré de voir le nombre de mot-clés du C# par exemple, où il y a une dizaine de façon différentes de protéger l'accès à une méthode de classe. Mauvais approche ! Ils ont pu jouer a "qui a la plus longue" avec Java qui avait moins de possibilité, mais au final, le perdant, c'est à mon avis le développeur. Quand tu vois qu'en Python, il y a à peu près 0 protections sur l'accès aux méthodes et qu'on arrive quand même à écrire des programmes, ça fait réfléchir.

    Si je regarde Python, au niveau de la construction du le langage, il s'en sort pas mal. Il y a eu pas mal de nouvelles constructions par rapport à la version 1.5.2 que j'avais apprise, mais elles s'intègrent syntaxiquement de façon assez fluide dans le langage: itérateurs, générateurs, yield from, string unicode, décorateurs, gestionnaires de contextes. Tout ça a ouvert vraiment la voie à un style de programmation plus évolué, tout en restant dans la simplicité initiale et la syntaxe du langage.

    Mettre C++ sur son CV ne veut maintenant plus rien dire. Quel C++ ? On est bien loin des 3 classes et deux constructeurs que j'ai appris à l'école.

    Mais bon, je râle mais je sais très bien que c'est inutile: selon la vieille théorie du "worse is better", on va se traîner C++ avec ses anciens et ses tous nouveaux problèmes encore très longtemps, tout comme Javascript…

    • [^] # Re: Intéressant

      Posté par  (Mastodon) . Évalué à 3.

      Je ne serais pas aussi pessimiste que toi. Certes il y a des évolutions mais, à mon sens, elles vont dans la bonne direction, à savoir écrire des choses puissantes en moins de lignes. Oui, ça crée pas mal de nouvelles syntaxes, mais au final, on s'y habitue assez vite et on les adopte. Les lambdas, au départ, c'est un peu chaud, mais dès qu'on en a écrit soi-même deux ou trois sur des cas assez simples (comme dans un tri avec std::sort), on comprends le truc et on les range dans la catégorie des bons outils à réutiliser.

      Après, est-ce qu'il faut les utiliser ? Non évidemment. Les exemples que j'ai pris ici, j'ai vu que ça passait bien et que ça simplifiait les choses, ça permet d'écrire moins de code pour l'utilisateur de la bibliothèque, respectant ainsi l'adage qui dit qu'en C++, écrire une bonne bibliothèque est difficile quand l'utiliser est facile.

      L'autre solution avec C++, c'est de se restreindre à un sous-ensemble et d'ignorer le reste. Après, il y a des trucs qui ne coûtent pas grand chose à faire et qui sont utiles pour les autres. Un exemple qui me vient en tête et que j'ai déjà utilisé, c'est d'écrire une méthode begin() et une méthode end() qui renvoient des itérateurs pour des classes qui ressemblent à des conteneurs. Pourquoi ? Parce qu'après, ça permet d'utiliser le range base for (le même que dans Java) sans aucun problème.

      • [^] # Re: Intéressant

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

        Bon, plutôt que de râler, je suis aller lire le lien proposé dans la dépêche sur les nouveautés de C++ (les dépêches techniques, c'est bien fait pour ça non ?)

        Je dois reconnaître qu'il y a plein de bonnes choses. Des constructions qui étaient régulièrement pénibles à mettre en oeuvre deviennent plus souples (initializer-list, initialisation des variables non statiques dans les classes, etc). Le langage va clairement être beaucoup moins rigide.

        Par contre, bonjour la croissance en complexité. Et j'en suis qu'au début.

        Par exemple, si vous pensiez que c'était compliqué de penser à la fois au constructeur par défaut, constructeur par copie et copie explicite, bienvenue dans le nouveau C++11 où il faudra aussi penser au constructeur par déplacement et copie par déplacement, et avoir en tête qu'une partie est généré automatiquement.

        Je vous laisse méditer 5 minutes sur cette petite phrase avant d'être sûr d'en avoir extrait la signification:
        If any move, copy, or destructor is explicitly specified (declared, defined, =default, or =delete) by the user, no move is generated by default. If any move, copy, or destructor is explicitly specified (declared, defined, =default, or =delete) by the user, any undeclared copy operations are generated by default, but this is deprecated, so don't rely on that.

        Pour ma part … hum hum (sourire gêné) … je vais avoir besoin de plus de 5 minutes pour aller lire ces histoires de move.

        • [^] # Re: Intéressant

          Posté par  (Mastodon) . Évalué à 5.

          Pour ma part … hum hum (sourire gêné) … je vais avoir besoin de plus de 5 minutes pour aller lire ces histoires de move.

          Pour comprendre l'intérêt des move, un petit exemple suffit :

          std::vector<double> func() {
            std::vector<double> vec;
          
            // mettre ici tout plein d'éléments dans vec
          
            return vec;
          }

          Avant, la dernière instruction provoquait une copie (sauf si le compilateur était intelligent, parfois) ce qui était idiot vu que vec, la variable locale à la fonction, va disparaître juste après le return. On copiait donc des données inutilement. Du coup, la sémantique move a été introduite et maintenant on peut dire : return std::move(vec); pour signaler au compilateur qu'on veut faire bouger ce vecteur, pas le copier. En interne, ça va juste déplacer le pointeur sur les données, ça ne va pas copier les données, et du coup, ça optimise vachement ton code.

          Donc, oui, il faut définir un constructeur et un opérateur d'affectation par déplacement, de manière à pouvoir bénéficier de cette sémantique. Bon, après, dans plein de cas, on peut faire une copie, ça ne mange pas de pain. Mais quand tu as besoin de ça, pour des grosses structures, c'est vraiment génial.

          • [^] # Re: Intéressant

            Posté par  . Évalué à 1.

            Il faut aussi ajouter que le std::move est lié au Rvalue-reference. Cela permet de "convertir" une Lvalue-reference (pour le compilateur) en Rvalue-reference

            Un autre exemple pourrait être (en se basant sur le tien), une fonction qui fait la moyenne

            double mean(const std::vector<double> &func) {
                /// blabla
                return 0;
            }

            Ça c'est bien mais essaye de faire ça

            mean(func());

            Tu auras le droit à un beau message d'erreur du compilateur (l'argument n'est pas un Lvalue-reference)
            Quelle était la solution?
            -Soit tu mettais le résultat de func dans une variable (une copie si il n'y a pas de RVO)
            -Soit tu ajoutais la fonction suivante

            double mean(std::vector<double> func) {
                /// blabla
                return 0;
            }

            Par contre la tu fais une copie.

            La solution avec les Rvalue-references est d'ajouter la fonction:

            double mean(const std::vector<double> &&func) {
            /// blabla
            return 0;
            }

            Tu peux maintenant utiliser:

            mean(func());
            • [^] # Re: Intéressant

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

              La première version a toujours été légale, tu peux binder une référence constante sur un temporaire.
              Par contre, avec une référénce non constante, oui, c'est incorrect et le passage par move peut aider.

            • [^] # Re: Intéressant

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

              Tu auras le droit à un beau message d'erreur du compilateur (l'argument n'est pas un Lvalue-reference)

              Comme dit zu, tu n'aura pas d'erreur. Ça passe très bien, et sans copie

              Sinon, la grande question est: faut il passer par valeur ou par référence?

              imagine un setteur qui en C++03 ressemble à ça:

              Contact::setIdentity(const string &firstname, const string &lastname, const string &address) {
                 m_first = firstname;  
                 m_last = lastname;
                 if (m_careAboutAddress)  
                        m_address = address;
              }

              Tu ne va pas te taper 6 variantes avec toutes les possibilité de rvalue ou value. Ce que tu fais c'est:

              Contact::setIdentity(string firstname,string lastname,string address) {
                 m_first = move(firstname); 
                 m_last = move(lastname);
                 if (m_careAboutAddress) 
                      m_address = move(address);
              }

              Comme ça, dans le meilleur des cas, il n'y a plus de copie (que des move), quand l'apellant peux faire des moves. Et dans le pire des cas, il n'y a toujours que 3 copies (comme avant). La différence c'est que ces 3 copies sont faite par l'appelant. (Ce qui a l'inconvénient de générer plus de code car chaque appelant doit faire les copie alors que avant les copies étaient mutualisées dans la fonction).

              Mais ce n'est toujours pas parfait, car si m_careAboutAddress est false, il n'y avait que 2 copies dans le pire des cas avant, alors que maintenant il y 3 copies dans le pire des cas. Bref, il n'y a pas de solution magique :-(

          • [^] # Re: Intéressant

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

            Pour reprendre du Ocaml, de base il retourne une référence (comme en java ?), mais on s'en fout de le savoir car il se débrouille. Vu que l'usage de variable modifiable en place, est un poil plus lourd, les variables sont créés une fois pour toute, et donc, fonctionner par référence est simple.

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

          • [^] # Re: Intéressant

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

            maintenant on peut dire : return std::move(vec);

            Pas besoin de mettre std::move lorsqu'on retourne des objects locaux.

    • [^] # Re: Intéressant

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

      Pour être utilisateur du C++ je partage certains avis. Je rigole même quand j'entends Bjarne Stroustrup voulant un langage "facile à apprendre". Ce n'est pas le cas, C++ est extrêmement compliqué. Et il le devient encore plus quand on joue avec des aspects poussés du C++ comme le principe SFINAE + std::enable_if. Honnêtement, je m'en sers jamais, à mes yeux ça représente plus souvent de la masturbation intellectuelle qu'autre chose.

      Par contre, il existe des choses vraiment bien en C++ et qui ne me feront pas retourner au C, comme le mot clé auto, les variadic templates, les lambdas, etc…

      Il est vraiment convivial de pouvoir faire :

      for (auto p : avector)
          p.use();
      

      Par contre, je pense que le nommage du C++ est toujours horrible, et je ne parle pas de std::enable_shared_from_this, oui oui c'est bien une classe.

      git is great because linus did it, mercurial is better because he didn't

      • [^] # Re: Intéressant

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

        En quoi le nommage C++ est horrible ? Qu'est qui est choquand dans std::enable_shared_from_this ?

      • [^] # Re: Intéressant

        Posté par  . Évalué à 3.

        std::enable_if sert surtout pour faire de la meta-programmation. Après si tu ne développes pas de composants génériques dans tes programmes, effectivement je vois pourquoi tu n'en vois pas l'interêt.
        Si tu veux un exemple réel pour voir l'intérêt de la chose(ou l'équivalent boost), regarde par exemple Firebreath (voir JSObject qui est une bibliothèque permettant d'exposer par l'intermédiaire d'un plugin NPAPI tout ce qu'il te chante.
        En utilisant (c'est assez acrobatique des fois) cette fonctionnalité, il est capable de convertir des types, qu'il ne connaît pas forcement, en objet javascript que tu pourras utiliser dans ta page par exemple. Exemple: si ta fonction retourne un type itérable il pourra par exemple le représenter sous forme de liste coté javascript.
        Effectivement c'est assez complexe, mais c'est le moindre mal quand tu veux faire quelque chose de générique, de la à dire que c'est de la masturbation…

      • [^] # Re: Intéressant

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

        Je connais pas du tout les fonctionalités avancées c++, je comprends donc à moitier le thread, mais est-ce que vous connaissez Ocaml? J'ai l'impression que beaucoup de choses sont en fait hyper simple en comparaison.

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

        • [^] # Re: Intéressant

          Posté par  (Mastodon) . Évalué à 2.

          mais est-ce que vous connaissez Ocaml? J'ai l'impression que beaucoup de choses sont en fait hyper simple en comparaison.

          J'ai fait un peu de caml dans ma jeunesse donc je ne sais pas si ça compte. Mais le fait que C++ (et d'autres langages d'ailleurs) introduise des concepts et des constructions orientées fonctionelles, ça le fait se rapprocher de langages purement fonctionnel. Maintenant, les deux restent suffisamment loin l'un de l'autre à mon sens.

          • [^] # Re: Intéressant

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

            Le type somme et le filtrage existe dans le nouveau c++ ?

            C'est tellement énorme comme concept, que je ne comprends pas que ce n'est pas plus commun. Rust va faire un carton, il ressemble tellement à ocaml avec une syntaxe C, que cela devrait bien marcher.

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

            • [^] # Re: Intéressant

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

              Le type somme et le filtrage existe dans le nouveau c++ ?

              Non, pas en tant que tel. On peut implémenter des choses qui "ressemblent", mais c'est évidemment horriblement verbeux et pas aussi puissant qu'un vrai type somme.
              Voir boost::variant par exemple. On utilisera un "visiteur" pour dispatcher selon le type interne (i.e. le constructeur en Ocaml).
              Ce genre de chose est un peu plus simple à implémenter en C++11. Mais je ne crois pas qu'il y'ait quelquechose de prévu dans la librairie standard, en tout cas par pour C++14 (on va déjà avoir std::optional, pas trop de révolution à la fois :D).

              • [^] # Re: Intéressant

                Posté par  (Mastodon) . Évalué à 4.

                on va déjà avoir std::optional, pas trop de révolution à la fois

                Et non ! Il a été enlevé au dernier moment et ne sera donc pas dans C++14. Il arrivera sans doute dans C++17. Et c'est bien dommage, ça aurait apporté un outil génial. Il y a dyn_array qui a subit le même sort.

            • [^] # Re: Intéressant

              Posté par  (Mastodon) . Évalué à 2.

              Le type somme et le filtrage existe dans le nouveau c++ ?

              Non, mais on a gagné le type std::tuple.

              • [^] # Re: Intéressant

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

                Tu mets sur le même niveau d'intérêt, le filtrage d'un type somme avec les tuples ?

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

                • [^] # Re: Intéressant

                  Posté par  (Mastodon) . Évalué à 2.

                  Non, mais les tuples sont une des fonctionnalités qu'on a gratos dans caml (si je ne me trompe pas) et qu'on n'avait pas dans C++ jusque là.

                  • [^] # Re: Intéressant

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

                    En effet, l'intérêt est surtout dans le retour de fonction avec arguments multiples. C'est le vrai grand intérêt.

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

        • [^] # Re: Intéressant

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

          Je ne fais pas autant d'Ocaml que je le souhaiterai, mais clairement, beaucoup de choses sont exprimable plus facilement en Ocaml qu'en C++ (que ce soit le polymorphisme paramétrique ou les types sommes). Après, je pense qu'on peut faire (mais est-ce qu'on veut vraiment :D) des choses avec les templates qui ne sont pas exprimables directement en OCaml (mais possible en MetaOcaml ou possiblement avec camlp4 / ppx).

          Malheuresement, OCaml reste moins bon en terme de taille de communauté, portabilité, librairies disponibles, performance …

          • [^] # Re: Intéressant

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

            "Malheuresement, OCaml reste moins bon en terme de taille de communauté, portabilité, librairies disponibles, performance … "

            Pour les performances, j'ai quand même un doute. Sur un code de calcul, oui, je suis d'accord. Sur un code de manipulation de symbole (compilo, transformation, etc…), c'est pas sûr du tout, sachant la facilité des filtrages des types sommes. Si la gestion de la mémoire devient complexe, je pense aussi que ocaml peut devenir plus rapide.

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

        • [^] # Re: Intéressant

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

          C'est sur que C++ est un langage compliqué et qu'on s'éloigne de la simplicité "je connais le C/Java/C#, je comprends le C++".

          OCaml souffre quand même deux ou trois problèmes si j'ai bien suivi:

          • c'est un paradigme différent et plus difficile à appréhender qu'un langage Objet impératif comme le C++. Pas mal de programmeurs formés aujourd'hui ne sont pas préparés à ce niveau d'abstraction (voire en sont complètement incapables).

          • il a l'air très bien en langage autonome, mais pour s'interfacer avec le monde extérieur, ca a l'air plus compliqué. Typiquement, si je veux faire un beau GUI portable, je vais me tourner vers Qt : je cherche sur les GUI supportés par OCaml, pas de Qt (tu m'étonnes, avec le C++, c'est pas gagné de transformer ça en logique pseudo-fonctionelle).

            C'est pas grave, allons voir Gtk en espérant que ça reste aussi portable. Bon, il y a LablGtk qui supporte Gtk 2.18 soit une version de Gtk qui est sortie en 2009. Même si le package a été mis à jour en décembre 2013, ça donne pas super confiance. J'ai pas l'impression que OCaml soit un bon choix pour une application qui a un GUI…

          • il parait aussi que la syntaxe est pas extra, au point qu'il existe des package pour des syntaxes alternatives…

          Sinon, j'avais fait du CamlLight et j'avais trouvé ça plutôt sympa bien qu'un peu bizarre.

          • [^] # Re: Intéressant

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

            "c'est un paradigme différent et plus difficile à appréhender qu'un langage Objet impératif comme le C++."

            C'est du vent ça. Franchement. Tu peux même coder objet impératif en Ocaml, si tu veux. Même si c'est un peu moche.

            "Typiquement, si je veux faire un beau GUI portable,"

            C'est claire. J'espère beaucoup à une vrai liaison avec les EFL. C'est le point noir de mon point de vue.

            "il parait aussi que la syntaxe est pas extra, au point qu'il existe des package pour des syntaxes alternatives…"

            Pas la syntaxe de base. Le metaocaml ou ocamlp4 sont simplement horrible. Dans la syntaxe de base, il y a quelques mots clef simple à comprendre (let, match, where, …). Les usages avancés utilisent plus de zigouigouis (genre des [> ou %% ), j'aime beaucoup moins. De base, c'est ultra lisible, mais pas habituelle (pas de parenthèse, pas de virgule).

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

          • [^] # Re: Intéressant

            Posté par  . Évalué à 1.

            Et aussi, OCaml ne supporte pas le calcul parallèle (sur plusieurs processeurs en même temps).

            • [^] # Re: Intéressant

              Posté par  . Évalué à 1.

              On peut toujours utiliser fork…

              C'est clair qu'Haskell est mieux pensé à ce niveau là (il est pensé tout court en fait, OCaml date d'une époque où l'on pensait que les processeurs allaient devenir de plus en plus rapides).

              Please do not feed the trolls

              • [^] # Re: Intéressant

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

                Il utilise le typage linéaire pour faire ça ? C'est très compliqué de faire un programme multithread, "safe" par construction.

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

            • [^] # Re: Intéressant

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

              C'est vrai aussi. Je crois qu'il y a une réflexion pour faire du multiprocessus avec du passage de message à faible cout. Il existe aussi un "map" parallèle qui fonctionne avec fork(), utile pour les très gros job.

              Le top du top serait juste un map/reduce (fold) à la google, utilisant du multithreading mais avec les threads déjà pret, pour éviter les couts de création. J'imagine que les problèmes de partages mémoires (et donc de la gestion des caches) rend la solution assez difficile.

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

            • [^] # Re: Intéressant

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

              Si, il existe de très bonne librairies pour faire ça. Par exemple parmap le fait sans changement dans le code source.

              Ça n'est pas inclus dans la librairie de base (qui est plutôt pauvre c'est vrai), mais de nombreuses librairies gravitent autour et font souvent de très bonnes choses.

          • [^] # Re: Intéressant

            Posté par  . Évalué à 4.

            c'est un paradigme différent et plus difficile à appréhender qu'un langage Objet impératif comme le C++. Pas mal de programmeurs formés aujourd'hui ne sont pas préparés à ce niveau d'abstraction (voire en sont complètement incapables).

            Je pense pas que le fonctionnel soit fondamentalement plus compliqué ou plus simple que l'objet ou l'impératif, il est surtout très différent et peut être un peu moins « passe partout ».

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

            • [^] # Re: Intéressant

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

              C'est pas faux, mais cela ne concerne pas Ocaml qui permet l'impératif (printf est plus simple qu'une monade j'imagine).

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

              • [^] # Re: Intéressant

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

                Ca c'est vraiment un argument à deux balles. Quel serait l'intérêt de faire un programme purement impératif et objet en OCaml ? Si c'est pour faire du OCaml autant tirer partie des points forts du langage…

                • [^] # Re: Intéressant

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

                  Parfois l'impératif est plus rapide (il peut éviter de créer des données intermédiaires), et c'est aussi un moyen de passé en douceur d'un modèle de programmation à l'autre.

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

    • [^] # Re: Intéressant

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

      Si je regarde Python, au niveau de la construction du le langage, il s'en sort pas mal. Il y a eu pas mal de nouvelles constructions par rapport à la version 1.5.2 que j'avais apprise, mais elles s'intègrent syntaxiquement de façon assez fluide dans le langage: itérateurs, générateurs, yield from, string unicode, décorateurs, gestionnaires de contextes. Tout ça a ouvert vraiment la voie à un style de programmation plus évolué, tout en restant dans la simplicité initiale et la syntaxe du langage.

      Je ne suis pas sûr que Python soit vraiment un langage référence de ce point de vue là. C'est certes moins verbeux, mais les différences entre python 1.5 et python 3.3 sont quand même gigantesques. En plus de ce que tu as cité, tu as aussi:
      - old-class vs new-class
      - compréhension liste, et autres extensions de syntaxes pour les dictionnaires et les ensembles
      - une API C cassé n-fois
      - nouvelle syntaxe pour le formatage
      - print() \o/

      Et quand on parle de python, on parle de python2 ou de python3 ? (je ne parle pas du code qui fonctionne en 2.7, en 3.3 mais pas en 3.2). Et on oubliera tous les trucs qui datent encore de la préhistoire de python (pourquoi len(), dir(), … ne sont pas des méthodes …).

      Quand tu vois qu'en Python, il y a à peu près 0 protections sur l'accès aux méthodes et qu'on arrive quand même à écrire des programmes, ça fait réfléchir.

      En assembleur, on arrive aussi à écrire des programmes :) Cet argument ne vaut pas grand chose. La question est la maintenabilité sur le long-terme, par des équipes diverses.

      • [^] # Re: Intéressant

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

        les différences entre python 1.5 et python 3.3 sont quand même gigantesques

        De fait, elles le sont. Mais la syntaxe est restée très homogène. Regarde la syntaxe lambda de C++, où la complexité de certaines définitions quand on commence à manipuler des functor, on est plus dans le même monde.

        En plus de ce que tu as cité, tu as aussi:
        - old-class vs new-class

        C'est complètement non intrusif au niveau de la syntaxe. Aujourd'hui, tu n'as même pas besoin de savoir que cette différence existe si tu lis du code Python.

        compréhension liste, et autres extensions de syntaxes pour les dictionnaires et les ensembles

        La première fois, la syntaxe est un peu étonnante, notamment quand tu as deux boucles et des if. Mais très vite, on remarque que:
        - la syntaxe est cohérente avec l'existant
        - la seule difficulté, c'est vraiment la règle des précédences entre les multiples for et if
        - la même syntaxe fonctionne pour les générateurs, les list-comprehenions, les set, les dictionnaires: ca veut dire que si tu as compris la syntaxe une fois, tu l'as compris pour tous les générateurs.

        • une API C cassé n-fois

        Moui, on sort un peu du langage cependant.

        • nouvelle syntaxe pour le formatage

        C'est celui où j'ai le plus de réserves.

        • print() \o/

        non, le plus méchant dans l'évolution de Python, c'est bien la gestion de l'unicode, le passage au byte-string.

        Si on regarde la question sous l'angle de "quelqu'un qui a appris Python 1.5 serait-il capable de comprendre un programme Python écrit en 3.4", on voit que :
        - il va buter sur les générateurs/list compréhensions
        - il va buter sur les décorateurs
        - il va buter sur les gestionnaires de contextes

        Bon, au final, il va ramer un peu c'est vrai, mais ça me semble moins violent que pour le C++.

        J'ai appris le C++ quand tout ce fratras de C++11 n'existait pas (je crois même pas qu'on osait lever une exception à l'époque) et Python en version 1.5.2 . En quelques heures, je me suis mis à niveau en Python. Pour C++, j'ai l'impression que je vais galérer beaucoup beaucoup plus.

        • [^] # Re: Intéressant

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

          De fait, elles le sont. Mais la syntaxe est restée très homogène. Regarde la syntaxe lambda de C++, où la complexité de certaines définitions quand on commence à manipuler des functor, on est plus dans le même monde.

          Je ne veux pas défendre C++ parce que je trouve que c'est langage assez moche, mais malheuresement relativement utile (en terme de ratio efficacité / abstraction / communauté). Pour les lambda, la syntaxe est relativement moche en C++ mais au moins on peut faire des choses avec (tandis que les lambda en python sont quand même ultra limités). Pour les functors, je ne vois pas de difficultés particulières. Vu les affres du passage de python2 à python3, la syntaxe a beau être "homogène" (pour autant que ça veuille dire quelquechose d'objectif), ce n'est pas facile de "changer" de versions. (ce n'est pas si difficile que ça sur des projets relativement petits toutefois).

          une API C cassé n-fois
          Moui, on sort un peu du langage cependant.

          C'est pas comme si on avait une spec de ce qu'était Python. Après CPython, sans son interface C (i.e. qu'avec du code Python pur), ça serait surement beaucoup moins utile (et utilisé). Donc il faut le prendre en considération.

          J'ai appris le C++ quand tout ce fratras de C++11 n'existait pas (je crois même pas qu'on osait lever une exception à l'époque) et Python en version 1.5.2 . En quelques heures, je me suis mis à niveau en Python. Pour C++, j'ai l'impression que je vais galérer beaucoup beaucoup plus.

          Probablement. C++ est un langage difficile, personne ne le niera. Je pense toutefois que ça va globalement dans le bon sens, i.e écrire du code C++11 correct est plus facile qu'écrire du code C++ 03 correct (quasiement pas besoin de pointeurs nues, quasiment pas besoin d'itérateurs, des conteneurs plus souples, …). Evidemment, il faut oublier C++ = C avec Classes qui est faux depuis bien longtemps.

        • [^] # Re: Intéressant

          Posté par  (Mastodon) . Évalué à 3.

          Mais la syntaxe est restée très homogène. Regarde la syntaxe lambda de C++, où la complexité de certaines définitions quand on commence à manipuler des functor, on est plus dans le même monde.

          Je pense que là, c'est une question d'habitude. Personnellement, par exemple, j'ai jamais compris la syntaxe de Perl, chaque fois que j'ouvre un script Perl, je n'ai aucune foutue idée de ce que ça peut faire. Pourtant, ça n'empêche pas certain de trouver Perl génial et bien foutu. Moi, la syntaxe C++, hormis les lambdas au début, je la trouve très cohérente. Et maintenant, même les lambdas, je les trouve plutôt bien intégrés.

          J'ai appris le C++ quand tout ce fratras de C++11 n'existait pas (je crois même pas qu'on osait lever une exception à l'époque) et Python en version 1.5.2 . En quelques heures, je me suis mis à niveau en Python. Pour C++, j'ai l'impression que je vais galérer beaucoup beaucoup plus.

          Le développement de Python s'est fait sur plusieurs années de manière assez incrémentale. Avec C++, on est passé de C++03 à C++11 (soit 8 ans de gestation) en une seule fois, donc la marche paraît plus haute mais elle ne l'est pas à mon sens.

        • [^] # Re: Intéressant

          Posté par  . Évalué à 5. Dernière modification le 13 février 2014 à 14:08.

          J'ai appris le C++ quand tout ce fratras de C++11 n'existait pas (je crois même pas qu'on osait lever une exception à l'époque) et Python en version 1.5.2 . En quelques heures, je me suis mis à niveau en Python. Pour C++, j'ai l'impression que je vais galérer beaucoup beaucoup plus.

          C++ : tu n'es pas obligé de te mettre à niveau. C'est un langage normalisé, il y a peu de normes, on trouve facilement des documentations complètes, et les compilateurs continueront à supporter chaque version des normes C et C++. Les versions successives du standard ne cassent pas le code (bon exceptionnellement une fonction peut changer de header mais ça ne va pas chercher bien loin).

          Python : Il parait que python 2 ne sera bientôt plus supporté. Moi je veux écrire du code réutilisable. Quel intérêt de programmer dans un langage qui ne compilera plus le jour où les développeurs décideront que la version n ne mérite plus d'être supportée ?
          Si je veux évoluer de Python 2 à 3 cela me coute un travail significatif à porter mon code ; je ne peux pas juste continuer à programmer comme j'ai l'habitude et utiliser deux ou trois nouvelles fonctionnalités qui me plaisent.

          • [^] # Re: Intéressant

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

            C++ : tu n'es pas obligé de te mettre à niveau.

            Si, je suis obligé de me mettre à niveau. Je travaille dans une entreprise, et des collègues utilisent C++11 . Si je comprends rien à leur code, ça va être difficile de coopérer.

            Mais bon, c'était juste une remarque générale, se mettre à niveau fait partie de la vie des informaticien normalement. C++ a toujours été un langage complexe, il le sera encore plus…

            • [^] # Re: Intéressant

              Posté par  . Évalué à 4. Dernière modification le 14 février 2014 à 14:36.

              Je parlais du cas où tu as un pouvoir de décision sur le langage pour ton projet. Dans le cas d'un plus gros projet en entreprise, ce n'est pas non plus à ton collègue de décider tout seul de commencer à coder en C++11, c'est une décision pour le management au début du projet, ou longuement discutée pendant le projet (C++11 dans KDE a été proposé dès 2011 ; deux ans plus tard on lisait toujours « So far, no C++11 feature was in KF5 »).

              Dans tous les cas ton entreprise a beaucoup d'intérêt dans C++ et que tu y travailles comme programmeur, tu utilisais déjà les fonctions de C++11 avant leur normalisation (par exemple dans Boost ou une extension GNU), voire c'est toi-même ou ton entreprise qui les as écrites et proposées au comité de normalisation.

              The major purpose of standardization os not to invent or develop something; it is to harmonize an existing practice.

              The standard is the result of many people and companies suggesting improvements and extensions […] Nobody was working as a full-time employee for the new C++ standard. The standard is not the result of a company with a big budget. Standards organizations pay nothing or almost nothing to people who work on developing standards. So, if a participant doesn't work for a company that has a special interest in the standard, the work is done for fun.

              Nicolai M. Josuttis, The C++ Standard Library, 2nd Ed. Pearson Education (2011), ISBN-13 978-0-321-62321-8. Citations de la section 2.1.1 page 8 et note 2.

            • [^] # Re: Intéressant

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

              Je travaille dans une entreprise, et des collègues utilisent C++11 . Si je comprends rien à leur code, ça va être difficile de coopérer

              Pareil si ils utilisent une nouvelle bibliothèque, ou une nouvelle version d'une bibliothèque. Et ce quelque soit le langage.

  • # Typo

    Posté par  . Évalué à 2.

    paramèyres

    => paramètres

  • # Typedef ?

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

    typedef std::function<EventStatus(Entity, EventType, Event*)> EventHandler;

    Typedef ? Qu'est-ce que ça veux dire encore ? C'est pas ce truc hérité du C ?

    Tu voulais dire:

    using EventHandler = std::function<EventStatus(Entity, EventType, Event*)>;

    L'avantage de using comparé à typedef est qu'il est sensé être plus intuitif, et il permet d'avoir de faire des alias vers des templates. Par souci d'uniformité, il vaux mieux utiliser using dans du code neuf.

    • [^] # Re: Typedef ?

      Posté par  (Mastodon) . Évalué à 3.

      Par souci d'uniformité, il vaux mieux utiliser using dans du code neuf.

      C'est vrai, tu as raison. J'utilise typedef par habitude mais je devrais utiliser using que je trouve beaucoup plus clair. Je vais l'ajouter à mon TODO.

      • [^] # Re: Typedef ?

        Posté par  . Évalué à 0.

        Malheureusement j'ai l'impression que Visual Studio ne connaît toujours pas ‘using’…

      • [^] # Re: Typedef ?

        Posté par  . Évalué à -1.

        Malheureusement, il Visual Studio 2013 ne supporte toujours pas l'utilisation les nouvelles utilisations du mot clef 'using'…

      • [^] # Re: Typedef ?

        Posté par  . Évalué à 1.

        Malheureusement, Visual Studio 2013 ne supporte toujours pas l'utilisation les nouvelles utilisations du mot clef 'using'…

      • [^] # Re: Typedef ?

        Posté par  (site web personnel) . Évalué à 5. Dernière modification le 14 février 2014 à 15:49.

        Malheureusement, Visual Studio 2013 ne supporte toujours pas l'utilisation les nouvelles utilisations du mot clef 'using'…

        • [^] # Re: Typedef ?

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

          Malheureusement, Visual Studio 2013 ne supporte toujours pas l'utilisation les nouvelles utilisations du mot clef 'using'

        • [^] # Re: Typedef ?

          Posté par  (Mastodon) . Évalué à 2.

          Et du coup, tu conseillerais plutôt de ne pas prendre en compte VS ? Je me pose la question, j'ai suivi des débats sur la mailing liste de LLVM et c'était pas triste de trouver un sous-ensemble de C++11 qui soit acceptable pour le développement de LLVM, et le problème venait toujours de VS. Ils reconnaissaient que ça s'améliorait avec les dernières versions mais il y a encore tout un tas de VS qui sont mauvais niveau C++11.

          Personnellement, je ne vais pas utiliser VS, même pour compiler pour Windows. Donc, j'en ai un peu rien à faire en vrai, mais j'aimerais avoir l'avis de quelqu'un qui a manifestement l'expérience du sujet.

Suivre le flux des commentaires

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