Crunchez vos adresses URL
|
Rejoignez notre discord
|
Hébergez vos photos
Page 92 sur 334 PremièrePremière ... 428284858687888990919293949596979899100102142192 ... DernièreDernière
Affichage des résultats 2 731 à 2 760 sur 10008
  1. #2731
    Citation Envoyé par rOut Voir le message
    des bouquins de Alexandrescu
    Tiens moi j'en ai lu des bouquins d'alexandrescu.
    Sauf que je ne me suis jamais servi des trucs à l'intérieur à part pour baver devant mon propre code.

    C'est ça le problème avec la métaprogrammation : t'as les concepts de base qui sont très pratiques, mais les concepts avancés (comme les listes à base de macros ou autres joyeusetés qu'on trouve dans boost.geometry ou boost.mpf) personne s'en sert, car ça sert presque à rien dans la pratique.

    En fait je pense que ça pourrait servir, mais en changeant totalement notre façon de coder. Plutôt que de s'embêter avec de l'héritage de tout le tsoin-tsoin il faudrait mettre pratiquement des templates partout.
    Mais après on tombe sur les problèmes classiques : messages d'erreur à rallonge, grosse lourdeur pour faire des trucs simples (par exemple pour appeller une fonction avec comme paramètres le contenu d'un tuple, je me suis tapé une centaine de lignes de code), compilation qui dure 10 ans, etc.

    À mon avis s'ils rajoutent le static_if, les concepts et les modules (d'ici 2017 ou 2023), ça pourrait devenir bien fun de ce côté-là.
    Rust fanboy

  2. #2732
    Perso je m'en sers. Avec parcimonie au boulot pour éviter de passer pour un freak, mais j'ai des projets persos entièrement basés dessus, par exemple pour implémenter des contraintes fonctionnelles vérifiées à la compilation (un peu comme les concepts, mais pour faire de la vérification des specifications fonctionnelles d'une API par exemple).

    Au boulot c'est par exemple pour optimiser un algorithme avec un déroulement de boucle ou pour faire du tag dispatch, un peu de PIMPL pour les API publiques, ou du polymorphisme statique. D'autres trucs comme par exemple le pattern "scope guard" d'Alexandrescu est un peu incontournable pour la gestion d'erreur.
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  3. #2733
    C'est pas de la métaprogrammation le scope guard et le pimpl
    Rust fanboy

  4. #2734
    Bin si, t'as un pattern templatisé et tu l'utilises partout.
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  5. #2735
    Eh ben, pour un vendredi après midi d'avant Noël, y'a du level
    Et pendant de temps, mes collègues se sont tous barrés...

  6. #2736
    Citation Envoyé par rOut Voir le message
    Pour les index dans un tableau, en fait l'astuce c'est que i++ va découpler la dépendance entre le résultat du ++ et l'indice du tableau.
    Quand on écrit a[++i], l'index du tableau est le résultat de l'addition et donc il faut faire l'addition, lire le résultat et seulement à ce moment le CPU va savoir à quel index lire.
    Tandis que pour a[i++], l'index dans le tableau correspond à la valeur courante de i, et l'addition peut se faire après ou en parallèle, ou être prédite, on n'aura besoin du résultat qu'à l'itération suivante. Il explique que c'est plus efficace.
    Si un compilateur produit un resultat plus rapide avec l'une ou l'autre variante, faut changer rapidement. Enfin je dis ca, j'ai pas teste, mais ca me parait completement aberrant ce que le type raconte.

    En parlant de fou furieux, y'a 10 ans j'avais assiste a une presentation de Scott Meyers. Je ne me souviens plus des details mais le gars etait etonnant. Il avait notamment un debit incroyable et restait malgre tout d'une limpidite parfaite meme pour un non bilingue

  7. #2737
    Citation Envoyé par vectra Voir le message
    Eh ben, pour un vendredi après midi d'avant Noël, y'a du level
    Et pendant de temps, mes collègues se sont tous barrés...
    Ce sont des mots pour se la péter, rien de plus

    Le pimpl ça permet de virer tes noms de variables du fichier .h pour les mettre dans le .cpp. Du coup si tu changes ces variables t'as juste à recompiler le .cpp et le .h ne change pas.

    Le scope guard ça permet d'exécuter un morceau de code à la fin d'une fonction, même en cas d'exception. Imagine par exemple tu fais un malloc() et juste en dessous tu rajoutes un scopeguard "free(pointeur)". Comme ca même si t'as 15 "return" et 34 "throw", ben le free sera toujours appelé.

    Le tag dispatch c'est tout simplement une classe ou une fonction qui prend un paramètre template, et dont le comportement varie en fonction de la valeur du paramètre.
    Par exemple : "template<bool UseLock> void fonction() { /* si UseLock est true, on va locker un mutex ; sinon non */ }"
    Sauf qu'en général on utilise des enum à la place du bool, comme ça on écrit "fonction<use_lock>" qui est plus clair que "fonction<true>".

    T'en as plein des trucs comme ça : RAII, CRTP, SFINAE, etc.

    Souvent quand je vois un truc de ce genre je suis obligé d'aller rapidement sur google pour me souvenir de ce que ça désigne, parce qu'une fois que t'as pris l'habitude d'un truc, ça te semble naturel et tu dis rarement pendant une conversion "je fais du RAII avec une classe qui hérite d'un CRTP dans cette fonction avec un template SFINAE".
    Rust fanboy

  8. #2738
    Le tag dispatch c'est pas tout à fait ça, c'est plutôt pour sélectionner une implémentation particulière suivant les caractéristiques du type passé à une fonction template.

    http://www.boost.org/community/gener...ag_dispatching
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  9. #2739
    Citation Envoyé par rOut Voir le message
    Avec parcimonie au boulot pour éviter de passer pour un freak, mais j'ai des projets persos entièrement basés dessus, par exemple pour implémenter des contraintes fonctionnelles vérifiées à la compilation (un peu comme les concepts, mais pour faire de la vérification des specifications fonctionnelles d'une API par exemple).
    C'est pas le genre de vérification qu'on fait habituellement avec des tests unitaires, ça ?

  10. #2740
    C'est pas ce que je voulais dire. Je fais ça dans le cadre d'un wrapper autour d'OpenGL et je vérifie que les combinaisons de paramètres passés aux fonctions sont compatibles avec ce que la spec décrit. Ca évite de compiler des choses qui ne donneront rien ensuite.
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  11. #2741
    Question : est-ce qu'il existe une sorte de standard implicite concernant les commentaires qui décrivent une fonction ? (les trucs de doxygen quoi)

    J'ai l'habitude depuis longtemps d'écrire /// \machin Truc bidule

    Mais j'ai vu récemment qu'en PHP il y avait des fonctions dans la librairie standard qui permettent de récupérer ce genre de commentaire, mais ça ne prend en compte que si c'est sous le format /** */ avec des * et des @ à l'intérieur.
    Ce sont les devs PHP qui sont un peu bornés, ou bien est ce que tout le monde écrit /** */ et je m'en rends pas compte ?
    Rust fanboy

  12. #2742
    C'est plus ou moins standardisé.

    En Java, la norme Javadoc est d'utiliser /** ... */ pour les commentaires bloc de documentation, et @ comme préfixe pour les mot clefs. Doxygen a également adopté ce format comme format possible pour les commentaires de doc en C++. Mais il accepte aussi les \ comme préfixes et d'autres formats de bloc comme /// ou //< ou /*<. C'est d'ailleurs configurable je crois. Javadoc non. Du coup /** et @ sont devenus standard de facto puisque commun aux divers outils de parsing de documentation.
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  13. #2743
    La déclinaison de JavaDoc pour PHP est PHPDoc. C'est pas le seul truc que PHP a piqué à Java...

  14. #2744
    Okay, je vois. Les mecs qui ont un background java écrivent /** */.


    Citation Envoyé par GrandFather Voir le message
    La déclinaison de JavaDoc pour PHP est PHPDoc. C'est pas le seul truc que PHP a piqué à Java...
    S'il y a bien un truc qui est fréquent dans le monde de la programmation c'est de piquer des trucs.
    D'ailleurs si vous savez pas quoi faire, vous pouvez coder un équivalent de composer pour C++. Succès garanti
    Rust fanboy

  15. #2745
    Pas forcément un background java, comme expliqué c'est utilisé assez couremment dans d'autres langages. C'est aussi reconnu par beaucoup d'outils, Eclipse par exemple détecte que les blocs en /** sont des blocs de doc tandis que /* sont des blocs normaux. Et puis je ne vois pas bien comment tu fais un bloc de doc avec ///, pour une ligne ok mais un bloc ? Aussi @ est sans doute moins ambigü que \ qui est avant tout le symbole universel d'echappement ou d'insertion de caractères spéciaux.

    Composer pour C++ serait sympa, pour peu qu'il soit facile d'installer des packages C++ sur de multiples plateformes. Certains tentent le coup déjà (0install), mais personellement quand j'y pense je me pencherai plus vers les outils de packaging et de dépendances existant déjà, comme dpkg par exemple, très puissant, car déjà intégré avec une tripotée d'outils de build automatique. Par exemple avec des trucs comme qemu-builder, tu peux compiler un package debian sur n'importe quel archi et version de debian supportée par qemu, avec tout en automatique : création du disque de la VM, installation du système de base, installation des dépendances préallables, compilation, création et signature du paquet et remise à zero de la VM de build.

    Faire ça soit même c'est s'attirer toutes les emmerdes du monde pour gérer les whatmille systèmes de build différents etc...
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  16. #2746
    Ben pour faire un bloc je mets plusieurs lignes l'une en dessous de l'autre :

    Code:
    /// \brief Fait ceci-cela
    /// \param blabla blabla
    /// \return blabla
    Après j'ai jamais vraiment utilisé doxygen, du coup je suis même plus certain que ça fonctionne.
    Mais j'ai pris l'habitude d'écrire à chaque fois "/// \brief fait ceci-cela" plutôt que "// fait ceci-cela", même si personne d'autre n'a l'air de faire ça.
    Je me dis "au cas où j'utilise doxygen sur ce code un jour, ça fera ça en moins à mettre".
    Rust fanboy

  17. #2747
    C'est quand même plus joli de faire:
    Code:
    /**
     * \brief Fait ceci-cela
     * \param blabla blabla
     * \return blabla
     */
    Comme tous les blocs de commentaires que l'on trouve avant la déclaration d'une fonction ou d'une classe... La aussi c'est un standard de-facto, la plupart des gens met des blocs avant les fonctions et les classes, et les commentaires one-line sont seulement utilisés à l'interieur des fonctions pour commenter le code, ou dans une classe pour documenter chaque membre.

    ---------- Post added at 20h25 ---------- Previous post was at 20h24 ----------

    Ca permet aussi de facilement rajouter de la doc plus longue, sans s'emmerder à rajouter des /// devant chaque ligne. Et un formatteur de code automatique peut même rajouter les * devant pour faire joli et aligner le tout automatiquement, alors qu'avec des commentaires one-line il ne sait pas vraiment s'il a le droit de modifier les retours à la ligne ou non
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  18. #2748
    Puisqu'on parle de métaprogrammation, j'aimerais essayer un truc (sans doute pas optimal et bien trop compliqué pour ce que c'est).

    En gros j'ai un automate qui peut faire plusieurs actions selon la commande qu'on lui passe. La commande est un type enuméré et me sert à indexer un tableau contenant des pointeurs de fonction vers les fonctions correspondant à l'action à effectuer pour la commande (les fonctions ont la même signature).

    Jusqu'ici tout va bien, mais je dois remplir mon tableau à la main lors de l'initialisation du programme, i.e. mettre le bon pointeur au bon index, alors que finalement, je sais combien de pointeurs j'ai à mettre dans mon tableau et je sais à quelles fonctions ils correspondent à la compilation. Du coup, ce que je veux faire serait juste d'avoir un objet qui connaissant le type énuméré va me remplir le tableau qui va bien tout seul. Si ça vous paraît possible, quelqu'un aurait-il des pointeurs (humour) sur par quel bout prendre cette horreur? J'ai vu qu'on pouvait faire de l'initialisation automatique de tableau statique, mais ça n'est pas vraiment suffisant pour ce que j'essaye de faire.
    Citation Envoyé par François
    L'ordinateur cantique, c'est l'avenir.

  19. #2749
    Faut bien que ton compilo sache par un moyen ou un autre comment associer un enum et un pointeur. Et ce sera pas par le nom à moins que tu aies envie d'utiliser des macros.

    Il suffit que tu mettes ta liste de fonctions dans le même ordre que ton enum, et c'est bon.

    Si par contre tu veux une syntaxe du genre "tableau = { { ENUM_F1, &f1 }, { ENUM_F2, &f2 }, etc. }", c'est possible avec les initializer_list du C++11 mais ce sera pas compile-time.
    Si tu veux cette syntaxe mais en plus que ce soit compile-time, il faudrait classer les valeurs d'enum par ordre croissant au moment de la compilation, ce qui doit être possible mais assez lourd à faire.
    Rust fanboy

  20. #2750
    Citation Envoyé par Thamior Voir le message
    Puisqu'on parle de métaprogrammation, j'aimerais essayer un truc (sans doute pas optimal et bien trop compliqué pour ce que c'est).

    En gros j'ai un automate qui peut faire plusieurs actions selon la commande qu'on lui passe. La commande est un type enuméré et me sert à indexer un tableau contenant des pointeurs de fonction vers les fonctions correspondant à l'action à effectuer pour la commande (les fonctions ont la même signature).

    Jusqu'ici tout va bien, mais je dois remplir mon tableau à la main lors de l'initialisation du programme, i.e. mettre le bon pointeur au bon index, alors que finalement, je sais combien de pointeurs j'ai à mettre dans mon tableau et je sais à quelles fonctions ils correspondent à la compilation. Du coup, ce que je veux faire serait juste d'avoir un objet qui connaissant le type énuméré va me remplir le tableau qui va bien tout seul. Si ça vous paraît possible, quelqu'un aurait-il des pointeurs (humour) sur par quel bout prendre cette horreur? J'ai vu qu'on pouvait faire de l'initialisation automatique de tableau statique, mais ça n'est pas vraiment suffisant pour ce que j'essaye de faire.
    Le problème que tu as c'est surtout que l'on n'a pas accès aux valeurs d'un enums dans un conteneur ou quelque chose que l'on peut parcourir à compile-time ou run-time. Du coup tu vas être obligé d'écrire l'enum, ET le tableau de valeurs de l'enum, ou le switch correspondant. Que ce soit via un dispatcher avec un switch, une version compile-time avec une liste de types, ou ton tableau de pointeurs de fonctions, tu vas devoir fournir la liste de valeurs de l'enum, pour le switch ou le tableau de mapping, ou quelque soit la technique utilisée, et tu auras donc deux listes à maintenir cohérentes.

    Une solution possible consiste à wrapper la déclaration de l'enum avec des macros C, qui vont générer la déclaration de l'enum mais également du code supplémentaire pour avoir une liste de valeurs toute prête. Par contre c'est moche à lire et on ne reconnait pas tout de suite un enum...
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  21. #2751
    Citation Envoyé par Tomaka17 Voir le message
    Ben pour faire un bloc je mets plusieurs lignes l'une en dessous de l'autre :

    Code:
    /// \brief Fait ceci-cela
    /// \param blabla blabla
    /// \return blabla
    Après j'ai jamais vraiment utilisé doxygen, du coup je suis même plus certain que ça fonctionne.
    Mais j'ai pris l'habitude d'écrire à chaque fois "/// \brief fait ceci-cela" plutôt que "// fait ceci-cela", même si personne d'autre n'a l'air de faire ça.
    Je me dis "au cas où j'utilise doxygen sur ce code un jour, ça fera ça en moins à mettre".
    C'est pile poil du Doxygen, et ça fonctionne.
    Et c'est ce que j'utilise moi-même parce que ça me saoule d'avoir une ligne de début de bloc et une de fin de bloc, je préfère cette syntaxe

    ---------- Post added at 10h36 ---------- Previous post was at 10h27 ----------

    Citation Envoyé par Thamior Voir le message
    Puisqu'on parle de métaprogrammation, j'aimerais essayer un truc (sans doute pas optimal et bien trop compliqué pour ce que c'est).

    En gros j'ai un automate qui peut faire plusieurs actions selon la commande qu'on lui passe. La commande est un type enuméré et me sert à indexer un tableau contenant des pointeurs de fonction vers les fonctions correspondant à l'action à effectuer pour la commande (les fonctions ont la même signature).

    Jusqu'ici tout va bien, mais je dois remplir mon tableau à la main lors de l'initialisation du programme, i.e. mettre le bon pointeur au bon index, alors que finalement, je sais combien de pointeurs j'ai à mettre dans mon tableau et je sais à quelles fonctions ils correspondent à la compilation. Du coup, ce que je veux faire serait juste d'avoir un objet qui connaissant le type énuméré va me remplir le tableau qui va bien tout seul. Si ça vous paraît possible, quelqu'un aurait-il des pointeurs (humour) sur par quel bout prendre cette horreur? J'ai vu qu'on pouvait faire de l'initialisation automatique de tableau statique, mais ça n'est pas vraiment suffisant pour ce que j'essaye de faire.
    Je n'ai pas exactement compris quel était ton soucis, mais tu échapperas difficilement à un code d'initialisation qui "connait" toutes tes fonctions.
    Les enum en C++ te donnent pas mal de contrôle, tu peux contrôler le type sous-jacent et les valeurs. Tu peux donc caster ton enum en entier, c'est pas folichon mais ça fonctionne, et utiliser l'entier comme index de ton tableau.
    Tu peux rendre ça un peu plus folichon en encapsulant proprement ton tableau et les fonctions d'accès dans une classe. Cette classe a un constructeur qui remplit le tableau; cette méthode est le seul endroit à modifier si tu veut ajouter de nouvelles fonctions.

  22. #2752
    Citation Envoyé par Thamior Voir le message
    En gros j'ai un automate qui peut faire plusieurs actions selon la commande qu'on lui passe. La commande est un type enuméré et me sert à indexer un tableau contenant des pointeurs de fonction vers les fonctions correspondant à l'action à effectuer pour la commande (les fonctions ont la même signature).
    Purée, j'avais commencé à faire ca pour borner les spécialisations de template, avant de me rendre compte que la surcharge était nettement plus adaptée. Par contre, c'est vraiment l'idée à suivre pour mon wrapper pourri, plutôt que d'enquiller les switch à répétition (j'ai fait un switch de switchs hier, bonjour l'horreur mais au moins ca marche).

    Quand j'aurai le temps, je pense que je mettrai en place la méthode suggérée par Mogluglu qui me semble de plus en plus claire à mesure que j'y repense. A savoir, une classe mère qui gère les opérations sans type (gestion de taille et des fichiers descriptifs) et dont toutes les méthodes type-dependantes seraient virtuelles pures. Ensuite, une classe fille à template dont chaque instanciation sur un type implémenterait les méthodes précédentes.

    Il y a encore quelques trous à remplir, mais je trouve que ca va dans la bonne direction (d'autant que c'est déjà ce que VTK fait il me semble): j'espère avoir le temps de faire ca au propre avant la rentrée.


    J'en profite pour remercier tous les contributeurs de leurs précieuses aides. Je sais que je n'ai pas forcément remercié tout le monde nommément, mais je note absolument toutes les solutions sur mon petit notepad et je les reprends dès que j'en ai l'occasion.

    Dans tous les cas, Noyeux Joël à vous les Canards

  23. #2753
    Citation Envoyé par vectra Voir le message
    je pense que je mettrai en place la méthode suggérée par Mogluglu qui me semble de plus en plus claire à mesure que j'y repense. A savoir, une classe mère qui gère les opérations sans type (gestion de taille et des fichiers descriptifs) et dont toutes les méthodes type-dependantes seraient virtuelles pures. Ensuite, une classe fille à template dont chaque instanciation sur un type implémenterait les méthodes précédentes.
    C'est un peu la base de la POO que tu décris là.
    En fait tu t'intéresses à la métaprogrammation mais tu connais même pas l'héritage :sigh:
    Rust fanboy

  24. #2754
    C'est bien ca, précisément.

    La métaprogrammation, on nous a bien mis ca dedans l'intérieur de nous quand on était en Maîtrise (les macros en lisp, il faut dire que c'est vraiment singulier). On a bien fait de l'objet en lisp (CLOS), mais jamais en langage impératif fortement typé. Il aurait fallu pour cela que je poursuive en DESS GénieLog, ce que je n'ai pas fait.

    Des cours génériques expédiés rapidement, c'est bien gentil, mais c'est par des études de cas concrètes qu'on entérine des acquis on qu'on répare les liens manquants. En l'occurence, je rattrape ca comme je peux. Le pire étant que je n'ai pas l'impression d'être mauvais en programmation quand je regarde mon entourage, notamment certaines promos d'ingénieurs en 3è année.

  25. #2755
    Bon... j'ai passé la matinée sur un truc rien que pour vous.
    C'est pas forcément super utile, mais je suis grave content de moi.

    D'abord la partie "générique" qui devrait marcher avec n'importe quel enum. C'est poilu, c'est à base de Boost.MPL et de variadic templates :
    http://codepad.org/RIhpDi1W
    Code:
    #include <boost/mpl/vector_c.hpp>
    #include <boost/mpl/range_c.hpp>
    
    #include <boost/mpl/find.hpp>
    #include <boost/mpl/transform.hpp>
    #include <boost/mpl/size.hpp>
    #include <boost/mpl/unique.hpp>
    #include <boost/mpl/sort.hpp>
    #include <boost/mpl/front.hpp>
    #include <boost/mpl/back.hpp>
    #include <boost/mpl/at.hpp>
    
    #include <array>
    #include <functional>
    #include <iostream>
    
    namespace enums
    {
      namespace bm = boost::mpl;
    
      namespace sequence
      {
    
        // the std::array type corresponding to the provided MPL sequence
        template< typename S > using array_type = std::array< typename bm::front< S >::type::value_type, bm::size< S >::type::value >;
    
        namespace detail
        {
          // recursive variadic template trick to build a sequence of indices
          template< size_t... Indices > struct indices           { using next = indices< Indices..., sizeof... (Indices) >; };
          template< size_t Size >       struct make_indices      { using type = typename make_indices< Size - 1 >::type::next; };
          template< >                   struct make_indices< 0 > { using type = indices< >; };
    
          template< typename S, size_t... Indices >
          constexpr array_type< S > to_array(indices< Indices... > const& i)
          { return {{ bm::at< S, bm::int_< Indices > >::type::value... }}; }
        }
    
        // simplify syntax
        template< typename S > using make_indices = typename detail::make_indices< bm::size< S >::type::value >::type;
    
        // automatically build the indices from the sequence and return the array
        template< typename S >
        constexpr array_type< S > to_array()
        { return detail::to_array< S >(make_indices< S >()); }
    
      }
    
      // specialize this getter to make your enum values available to all the other structures
      template< typename E >
      struct get_values { typedef bm::vector< > type; };
      template< typename E > using values_of = typename get_values< E >::type;
    
    
      // transformer that will replace values that are in the provided sequence by their index, or by 1 past the end of the sequence if the value is not found.
      template< typename S >
      struct make_index
      {
        using value_type = typename bm::front< S >::type::value_type;
        template< size_t V > using key   = bm::integral_c< value_type, value_type(V) >;
        template< size_t V > using index = bm::integral_c< size_t, size_t(V) >;
    
        template< typename T > struct apply { 
          using iterator  = typename bm::find< S, key< T::value > >::type;
          using not_found = index< bm::size< S >::type::value >;
          using position  = index< iterator::pos::value >;
    
          typedef typename bm::if_< boost::is_same< iterator, bm::end< S > >, not_found, position >::type type;
        };
      };
    
    
      // structure that will be in charge of creating arrays of enum values as well as an optimized table of enum<->index mapping
      template< typename E, typename S = values_of< E > >
      struct indexer {
        struct detail {
          // sort and make the values unique
          using values = typename bm::unique< typename bm::sort< S >::type, bm::equal_to< bm::_1, bm::_2 > >::type;
    
          static constexpr size_t begin = bm::front< values >::type::value;
          static constexpr size_t end   = bm::back< values >::type::value + 2;
          static constexpr size_t size  = end - begin;
    
          // create a range for all the possible values
          using range        = bm::range_c< size_t, begin, end >;
          using index_source = typename bm::copy< range, bm::back_inserter< bm::vector_c< size_t > > >::type;
    
          // then build the index, mapping the enum value to its index in the array
          using index  = typename bm::transform< index_source, make_index< values > >::type;
        };
    
        using values_type = typename sequence::array_type< typename detail::values >;
        using index_type  = typename sequence::array_type< typename detail::index >;
    
        static constexpr values_type values = sequence::to_array< typename indexer< E, S >::detail::values >();
        static constexpr index_type  index  = sequence::to_array< typename indexer< E, S >::detail::index >();
      };
    
    
      // utility function to extend std::function features with an "not implemented" default function.
      template< typename R, typename... Ts >
      struct signature : std::function< R(Ts...) > {
        signature(R(f)(Ts...)) : std::function< R(Ts...) >(f) {}
        static R not_implemented(Ts... a) { std::cout << "Not implemented." << std::endl; return R(); }
      };
    
    
      // specialize this getter to make your enum handler signature available to all the other structures
      template< typename E >
      struct get_signature { typedef signature< void > type; };
      template< typename E > using signature_of = typename get_signature< E >::type;
    
      // specialize this getter to make your enum default handler available to all the other structures
      template< typename E >
      struct get_default { static signature_of< E > const& value; };
    
    
      // default' default implementation is to call the corresponding signature's not_implemented function
      template< typename E >
      signature_of< E > const& get_default< E >::value = signature_of< E >::not_implemented;
    
    
      // specialize this getter to bind your enum values to their handlers
      template< typename E, E C >
      struct get_handler { struct type { using value_type = signature_of< E >; static signature_of< E > const& value; }; };
    
    
      // default' handler for any unbound value is the get_default< E > handler
      template< typename E, E C >
      signature_of< E > const& get_handler< E, C >::type::value = get_default< E >::value;
    
    
      // transformer that will build a sequence of the handlers bound to each enum values
      template< typename E, typename S >
      struct make_dispatcher
      {
        using value_type = typename bm::front< S >::type::value_type;
    
        template< typename T > struct apply { typedef typename get_handler< E, value_type(T::value) >::type type; };
      };
    
    
      // structure that will be in charge of providing handlers array and ways to access the correct handler from an enum value
      template< typename E, typename S = values_of< E > >
      struct dispatcher {
        struct detail {
          typedef typename bm::front< S >::type::value_type value_type;
    
          using enum_indexer  = indexer< E, S >;
          using enum_handlers = typename bm::transform< typename indexer< E, S >::detail::values, make_dispatcher< E, S > >::type;
    
          // add a default handler at the end of the sequence for any unknown enum values
          using handlers      = typename bm::push_back< enum_handlers, typename get_handler< E, E(enum_indexer::detail::size) >::type >::type;
    
          // clamp the index so it is limited to actual handlers or to the default one at the end of the sequence, also use the offset introduced in the index table
          static size_t constexpr clamp(size_t const i) { return enum_indexer::index[std::min(i - enum_indexer::detail::begin, 0 + enum_indexer::detail::size - 1)]; }
        };
    
        using handlers_type = typename sequence::array_type< typename detail::handlers >;
        static const handlers_type handlers;
    
        // accessor that will do all the operations to map an enum value to an handler
        inline signature_of< E > const& operator[](typename detail::value_type const e) { return dispatcher::handlers[detail::clamp(e)]; }
      };
    
      template< typename E, typename S >
      const typename dispatcher< E, S >::handlers_type dispatcher< E, S >::handlers = sequence::to_array< typename dispatcher< E, S >::detail::handlers >();
    
    }
    Ensuite la partie use-case avec l'enum en question de comment mapper tel opcode à tel handler :
    http://codepad.org/5OxQ2e6b
    Code:
    enum op_code
    {
        DO_OPERATION_1 = 6,
        DO_OPERATION_2 = 12,
        DO_OPERATION_6 = 12,
        DO_OPERATION_BLA = 7,
        DO_OPERATION_UNBOUND = 8,
    };
    
    namespace enums
    {
      // declaration of the enum values in an MPL vector
      template<> struct get_values< op_code > { typedef boost::mpl::vector_c< op_code, DO_OPERATION_1, DO_OPERATION_2, DO_OPERATION_BLA, DO_OPERATION_UNBOUND > type; };
    
      // declaration of the handlers signatures for our enum
      template<> struct get_signature< op_code > { typedef signature< void, std::string const&, std::string const& > type; };
    }
    
    // declaration of the handlers and binding to the dispatcher
    void do_opcode_1(std::string const& param1, std::string const& param2) { std::cout << "doing opcode 1" << std::endl; }
    template<> enums::signature_of< op_code > const& enums::get_handler< op_code, DO_OPERATION_1 >::type::value = do_opcode_1;
    
    void do_opcode_2(std::string const& param1, std::string const& param2) { std::cout << "doing opcode 2" << std::endl; }
    template<> enums::signature_of< op_code > const& enums::get_handler< op_code, DO_OPERATION_2 >::type::value = do_opcode_2;
    
    void do_opcode_bla(std::string const& param1, std::string const& param2) { std::cout << "doing opcode bla" << std::endl; }
    template<> enums::signature_of< op_code > const& enums::get_handler< op_code, DO_OPERATION_BLA >::type::value = do_opcode_bla;
    
    
    int main(int argc, char const *argv[])
    {
      typedef enums::indexer< op_code > op_code_indexer;
      typedef enums::dispatcher< op_code > op_code_dispatcher;
    
      {
        std::cout << "indexer::values array:" << std::endl;
        size_t i = 0;
        op_code_indexer::values_type constexpr values = op_code_indexer::values;
        for(auto const& e : values) {
          std::cout << "  " << i << ": " << e << std::endl;
          i++;
        }
      }
    
      {
        std::cout << "indexer::index array:" << std::endl;
        size_t i = op_code_indexer::detail::begin;
        op_code_indexer::index_type constexpr index = op_code_indexer::index;
        for(auto const& e : index) {
          std::cout << "  " << i << " -> " << e << std::endl;
          i++;
        }
      }
    
      std::cout << "handlers addresses:" << std::endl;
      std::cout << "  do_opcode_1:     " << reinterpret_cast< void* >(&do_opcode_1) << std::endl;
      std::cout << "  do_opcode_2:     " << reinterpret_cast< void* >(&do_opcode_2) << std::endl;
      std::cout << "  do_opcode_bla:   " << reinterpret_cast< void* >(&do_opcode_bla) << std::endl;
      std::cout << "  not_implemented: " << reinterpret_cast< void* >(&enums::signature_of< op_code >::not_implemented) << std::endl;
    
      {
        std::cout << "dispatcher::handlers array:" << std::endl;
        size_t i = 0;
        for(auto const& e : op_code_dispatcher::handlers) {
          std::cout << "  " << i << ": " << reinterpret_cast< void* >(*e.target< decltype(&enums::signature_of< op_code >::not_implemented) >()) << std::endl;
          i++;
        }
      }
    
      op_code_dispatcher dispatcher;
      dispatcher[DO_OPERATION_1]("foo", "bar");
      dispatcher[DO_OPERATION_2]("foo", "bar");
      dispatcher[DO_OPERATION_BLA]("foo", "bar");
      dispatcher[DO_OPERATION_UNBOUND]("foo", "bar");
    
      dispatcher[op_code(123)]("foo", "bar");
    
      return 0;
    }
    L'output:
    Code:
    indexer::values array:
      0: 6
      1: 7
      2: 8
      3: 12
    indexer::index array:
      6 -> 0
      7 -> 1
      8 -> 2
      9 -> 4
      10 -> 4
      11 -> 4
      12 -> 3
      13 -> 4
    handlers addresses:
      do_opcode_1:     0x401150
      do_opcode_2:     0x4010e0
      do_opcode_bla:   0x401070
      not_implemented: 0x4011c0
    dispatcher::handlers array:
      0: 0x401150
      1: 0x401070
      2: 0x4011c0
      3: 0x4010e0
      4: 0x4011c0
    doing opcode 1
    doing opcode 2
    doing opcode bla
    Not implemented.
    Not implemented.


    L'avantage c'est qu'on a juste besoin de l'enum, d'un mpl::vector contenant les valeurs de l'enum (même pas besoin de trier, c'est fait tout seul, et les valeurs en double sont aussi gérées avec le même handler), et de la signature des handlers. Cette partie là peut même se générer super facilement via une macro qui wrappe la déclaration de l'enum.

    Ensuite, pour chaque fonction handler, il faut l'enregistrer comme handler pour tel ou tel valeur de l'enum. On peut même facilement enregistrer le même handler pour plusieurs valeurs de l'enum, ou pour plusieurs enums différents. Ca a l'avantage de découpler la liste des valeurs d'un éventuel switch qu'il faudrait maintenir avec à la fois la bonne liste de valeurs et les bons mappings.

    Ca gère les op-code inconnus avec un callback par défaut automatiquement bindé à toutes les valeurs inconnues, et c'est certainement vachement optimisé car basé sur des tableaux compile-time (ouais en fait j'en sais rien).
    Pour voir le machin en action, il suffit de mettre le second fichier en dessous de l'autre dans un .cpp et de compiler avec "gcc -std=c++11" et avec boost >= 1.49 installé quelque part.

    Niveau implémentation, en fait j'ai une première structure qui va me créer un std::array avec les valeurs de l'enum (indexer::values), et un deuxième de mapping inverse (indexer::index) avec en index les valeur de l'enum et en contenu l'index où cette valeur est positionnée dans indexer::values, ou rempli avec 1 + la plus grande valeur de l'enum si l'enum ne contient rien à cet index. Pour économiser de la place, cet index est décalé par rapport à la valeur de l'enum la plus petite.

    Ensuite, j'ai une autre structure dispatcher, qui va re-créer un tableau similaire à indexer::values, mais au lieu des valeurs de l'enum, celui-ci contient des std::function vers chacun des handlers enregistrés (qui sont récupérés via l'implémentation par défaut de get_handler ou celle spécialisée par l'utilisateur et associant une fonction à une valeur de l'enum). Et il utilise ensuite le tableau indexer::index pour récupérer le bon handler à partir de la valeur de l'enum passée à l'opérateur [].

    ---------- Post added at 16h30 ---------- Previous post was at 16h22 ----------

    Edit: J'ai pas vraiment tuné le bouzin, comme je disais je n'ai pas non plus vérifié qu'il ne générait pas des trucs inutiles. Il y a peut être moyen de rajouter des constexpr partout pour éviter qu'il mette du code au run-time.
    Dernière modification par rOut ; 24/12/2012 à 20h10.
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  26. #2756

  27. #2757
    C'est cadeau. Mais va pas me le casser hein .
    "Dieu est mort" · "Si le téléchargement c’est du vol, Linux c’est de la prostitution."

  28. #2758
    Je vais tâcher de.

    J'ai pas mal de lectures à faire avant de comprendre comment ca fonctionne, mais c'est précisément le but. Quand ma thèse sera finie, faudra bien que je me trouve un travail, et je me doute bien que ca va finir en analyste-programmeur. Donc, au boulot!

    En attendant, merci encore, et surtout passez de bonnes fêtes :trucsdenoel:

  29. #2759
    Mer il et fou. Cadeau empoisonné s'il en est (Je vais passer ma journée à essayer de comprendre tout ça)
    Citation Envoyé par François
    L'ordinateur cantique, c'est l'avenir.

  30. #2760
    Citation Envoyé par vectra Voir le message
    Je vais tâcher de.

    J'ai pas mal de lectures à faire avant de comprendre comment ca fonctionne, mais c'est précisément le but. Quand ma thèse sera finie, faudra bien que je me trouve un travail, et je me doute bien que ca va finir en analyste-programmeur. Donc, au boulot!


    Je crois que t'as pas encore compris qu'il y a maximum 2-3 % des programmeurs C++ dans le monde qui s'emmerdent avec du code pareil.

    Ce code fait une centaine de lignes, plus une autre vingtaine de lignes à l'utilisation, il est super chiant à lire et à utiliser, le tout pour... générer un tableau !
    À la place tu aurais pu écrire "pointeur* tableau[] = { element1, element2, element3, ... };"
    Cay nul !

    La métaprogrammation c'est des trucs de doctorant ou de développeur de librairie super compliquée. Ca ne te servira à rien dans un vrai boulot.
    Par contre l'héritage, la polymorphie et tout ça, ça te servira.
    Rust fanboy

Page 92 sur 334 PremièrePremière ... 428284858687888990919293949596979899100102142192 ... DernièreDernière

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •