Partage
  • Partager sur Facebook
  • Partager sur Twitter

Enumerations et operations bit à bit.

    1 novembre 2021 à 21:27:12

    Bonjour,

    Supposons que j'ai l'enumération suivante:

    enum class Category
    {
    	None = 0,
    	Player = 1 << 0,
    	Allied = 1 << 1,
    	Ennemy = 1 << 2,
    	Air = 1 << 3,
    	Ground = 1 << 4
    };

    Chaque valeur met à Vrai un seul bit (tous distinct).
    Je souhaite pouvoir combiner ces valeurs, pour délivrer un message a toutes les entités alliés, ou toutes les entités ennemies qui évoluent dans les airs (par exemple).

    Le première solution que j'avais mis en œuvre consistait à convertir les valeurs en entiers pour effectuer des opérations bit à bit (operateur or).
    Mais, si par la suite, je fait une erreur sur l'entier résultant, ça me fera un bug méchant qui ne sera surtout pas signalé par le compilateur, et probablement difficile à identifier.
    Par exemple:

    unsigned category = static_cast<unsigned>(Category::Ennemy) | static_cast<unsigned>(Category::Air);
    
    // code quelconque
    
    ++category;    // Non sens ici, et ça compile.

    J'ai pensé convertir en std::bitset, mais la taille de ce dernier est conditionné au nombre de valeurs de l'énumération, et je ne connait aucun moyen de lier l'un avec l'autre.

    Enfin, j'ai fait une tentative avec la surcharge de l'operateur or, mais ce n'est pas au point non plus:

    // Example program
    #include <iostream>
    
    enum class Category
    {
    	None = 0,
    	Player = 1 << 0,
    	Allied = 1 << 1,
    	Ennemy = 1 << 2,
    	Air = 1 << 3,
    	Ground = 1 << 4
    };
    
        // combinaisons autorisées
    enum class ResultCategory
    {
        None = 0,
    	Player = 1 << 0,
    	Allied = 1 << 1,
    	Ennemy = 1 << 2,
    	Air = 1 << 3,
    	Ground = 1 << 4,
    	PlayerAllied = Player | Allied,
    	AirEnnemy = Ennemy | Air,
    	GroundEnnemy = Ennemy | Ground
    }
    
    ResultCategory operator|(Category lft, Category rgt)
    {
        return static_cast<ResultCategory>(static_cast<unsigned>(lft) | static_cast<unsigned>(rgt));
    }
    
    int main()
    {
    	std::cout << static_cast<unsigned>(Category::Player | Category::Allied);
    }
    
    • Partager sur Facebook
    • Partager sur Twitter
      2 novembre 2021 à 2:15:17

      Quand on joue avec les bits de cette façon, on ne fait pas d'addition sur le bitset (qu'il soit explicite ou pas).
      On ne fait que des opérations bit à bit comme | & ^ ou ~ (en faisant attention sur l'objet sur lequel on agit).
      Faire de l'assembleur en C++ ce n'est pas évident ...

      Je me suis amusé à retrouver les opérations principales.
      bitmap: ensemble des bits de départ.
      mask: ensemble des bits sur lesquels on veut agir.
      Ajouter: bitmap = bitmap | mask;
      Extraire: bit = bitmap & mask;
      Inverser: bitmap = bitmap ^ mask;
      Effacer: bitmap = bitmap ^ (bitmap & mask);

      -
      Edité par PierrotLeFou 2 novembre 2021 à 2:49:52

      • Partager sur Facebook
      • Partager sur Twitter

      Le Tout est souvent plus grand que la somme de ses parties.

        2 novembre 2021 à 7:57:15

        Salut,

        Deedolith a écrit:

        J'ai pensé convertir en std::bitset, mais la taille de ce dernier est conditionné au nombre de valeurs de l'énumération, et je ne connait aucun moyen de lier l'un avec l'autre.

        Héhé ... Faut il te rappeler que les valeurs énumérées sont des constantes de compilation?

        Et si ta constante de compilation (ta valeur énumérée) correspondait en réalité à ... l'indice d'un bit particulier dans un bitset?  Pour autant que tu dispose d'une constante de compilation représentant le nombre total de bits qu'il te faut, tu pourrais très bien être sauvé, ne crois tu pas?

        Après, nous pourrions juste ajouter quelques sucres magiques par-ci par là pour rendre les choses faciles à utiliser ;)

        Allez, je te montre de quoi je parle :

        /* on utilise la mécanique normale des énumération 
         */
        enum class Category
        {
            Player,
            Allied,
            Ennemy,
            Air,
            Ground,
            Max,
        	error
        };
        
        /* tout ce qu'il nous faut, c'est le moyen de transformer une
         * valeur énumérée fortement typée en valeur entière non signée
         * susceptible de représenter un indice
         */
        template <typename T>
        constexpr size_t toIndex(T value){
            static_assert(std::is_enum<T>::value,
                          "Parameter type should be an enumeration");
            return static_cast<size_t>(value);
        }
        
        template <typename T>
        struct CategoryTrait{
            static_assert(std::is_enum_v<T>,
                          "Parameter type should be an enumeration");
            using category = T;
            using bit_array = std::bitset<toIndex(T::Max)>;
        	static constexpr bit_array& set(bit_array & array, T pos, bool value = true){
        		array.set(toIndex(pos), value);
        		return array;
        	}
        	static constexpr bit_array& reset(bit_array & array, T pos){
        		array.reset(toIndex(pos));
        		return array;
        	}
        	static constexpr bool get(bit_array & array, T pos ){
        		return array[toIndex(pos)];
        	}
        };
        
        template <typename T,
                  typename trait=CategoryTrait<T>>
        typename trait::bit_array & set(typename trait::bit_array & array, T pos, bool value = true){
        	return trait::set(array, pos, value);
        }
        template <typename T,
                  typename trait=CategoryTrait<T>>
        typename trait::bit_array & reset(typename trait::bit_array & array, T pos){
        	return trait::reset(array, pos);
        }
        template <typename T,
                  typename trait=CategoryTrait<T>>
        constexpr bool get(typename trait::bit_array & array, T pos){
        	return trait::get(array, pos);
        }
        using CatArray = CategoryTrait<Category>::bit_array;
        int main(){
        	CatArray array;
        	set(array,Category::Player);
        	std::cout<<get(array,Category::Player)<<"\n";
        	set(array,Category::error); // lance une exception (j'aurais préféré une assertion, mais bon...)
        	std::cout<<get(array,Category::error)<<"\n"; // IDEM
        }

        Avoue que c'est cool, non? 

        -
        Edité par koala01 2 novembre 2021 à 8:41:24

        • Partager sur Facebook
        • Partager sur Twitter
        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
          2 novembre 2021 à 10:39:29

          Il suffit d’enlever "class" dans "enum class". Pour les potentielles erreurs, tu peux probablement mettre des assert et de toute manière il vaut mieux se concentrer sur la résolution du problème que chercher à prévenir des erreurs que tu ne fera probablement pas (tips: il ne faut pas coder à 3h du mat avec une bouteille de whisky à côté)

          -
          Edité par JadeSalina 2 novembre 2021 à 10:44:10

          • Partager sur Facebook
          • Partager sur Twitter
            2 novembre 2021 à 12:16:09

            JadeSalina a écrit:

            Il suffit d’enlever "class" dans "enum class".

            Pourquoi donc voudrais tu avoir une énumération "simple" au lieu d'une énumération fortement typée ?

            As-tu seulement conscience des avantages que peuvent apporter les énumération fortement typées par rapport aux énumérations classiques ou n'exprimes tu -- encore une fois -- qu'un avis sans recul forgé au travers de la video d'un gourou quelconque?

            Et si ces avantages te semblent inutiles, pourquoi ne pas suivre la logique jusqu'au bout et passer "simplement" par des valeurs numériques définies sous la forme de

            const size_t Player = 1;
            const size_t Allied = 2;
            const size_t Ennemy = 3;
            const size_t Air = 4;
            const size_t Ground = 5;
            

            voire, carrément, terminer avec de simples

            #define Player 1
            #define Allied 2
            #define Ennemy 3
            #define Air 4
            #define Ground 5

            Mais as tu seulement la moindre idée de ce que tu auras perdu à chacune de ces étapes?

            JadeSalina a écrit:

            Pour les potentielles erreurs, tu peux probablement mettre des assert

            Même pas, parce que un assert ne montrera son effet que lors de l'exécution... J'aurais largement préféré une erreur à  la compilation, qu'il est tout à fait possible d'obtenir, soit dit en passant ;)

            JadeSalina a écrit:

             et de toute manière il vaut mieux se concentrer sur la résolution du problème que chercher à prévenir des erreurs que tu ne fera probablement pas

            Ah, voilà exactement la preuve que tu manque décidément encore énormément de recul quant au développement informatique.

            Car cette phrase met bel et bien en évidence le fait que tu es persuadé que tu ne fera jamais la moindre erreur.

            Avec un peu de temps, tu te rendras compte que

            la loi de Murphy dit:

            l'utilisateur est un imbécile distrait qui n'attend que l'occasion de faire une connerie

            et que, au  risque de te rabaisser dans ta propre estime de toi-même, je tiens à  te préciser que  tu ne vaux absolument pas mieux que n'importe quel utilisateur à ce sujet (mais, rassure toi, moi non plus... La seule différence entre toi et moi, c'est que j'en suis conscient :D)

            C'est fini le monde des bisounours... Bienvenue dans la triste réalité du monde réel ;)

            Enfin, bref, tout cela pour dire que je préfères, et de loin, mettre tous les garde-fous possibles pour éviter qu'une erreur ne passe inaperçue, si bien que, dans la mesure du possible
            1. je m'assurerai que  toute erreur susceptible d'être identifiée à  la compilation le sera, parce que le code qui produit l'erreur sera -- a priori -- encore bien frais dans la mémoire de celui qui l'a écrit
            2. je veillerai à ce que si une erreur n'est pas identifiée à  la compilation, elle ait le maximum de chances d'être identifiée lors de la période de tests, car cela évitera de m'éviter de me retrouver avec un tas de comportements basés sur cette erreur et qu'il faudra corriger "par la suite"
            3. dans le pire des cas, j'espérerai que les erreurs rencontrées par l'utilisateur final remontent jusqu'à moi "le plus vite possible" pour la même raison que le (2), mais aussi parce que plus le temps passe, plus j'ai de chances d'avoir oublié tous les raisonnements logiques qui m'ont mené à prendre les décisions que j'ai prises lors du développement
            • Partager sur Facebook
            • Partager sur Twitter
            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
              2 novembre 2021 à 13:48:15

              JadeSalina a écrit:

              Il suffit d’enlever "class" dans "enum class". Pour les potentielles erreurs, tu peux probablement mettre des assert et de toute manière il vaut mieux se concentrer sur la résolution du problème que chercher à prévenir des erreurs que tu ne fera probablement pas (tips: il ne faut pas coder à 3h du mat avec une bouteille de whisky à côté)

              -
              Edité par JadeSalina il y a environ 1 heure


              C'est justement ce que je veux éviter (regarde mon post de départ, j'y ai indiqué un contre exemple), car les énumérations "classique" se dégradent automatiquement (et implicitement) en entier, ce qui, à l'instar des tableaux "style C" se dégradants en pointeurs nu, pose problème (perte du type, ouverture aux conneries dont l'immondicité dépasse l'entendement etc ...).
              • Partager sur Facebook
              • Partager sur Twitter
                2 novembre 2021 à 14:44:04

                J’avais pas lu le post en entier. Tu es en train d’essayer de t’empecher de faire ++ sur la catégorie ?? Bah pourquoi le faire en premier lieu, si c’est pas ce que tu veux faire ? À quel moment tu peux faire ce genre d’erreur (sauf bourré à 3h du mat) ? Sinon tu peux simplement récupérer la catégorie avec les ou binaires et stocker le résultat dans une variable const comme ça tu ne peux pas la modifier. D’ailleurs pourquoi mettre const puisque ça nous empêche pas de const_cast, et ça ne sera pas capturé à la compilation ! Eh bien tout simplement, on n’utilise pas const_cast, et non ce n’est pas le compilateur qui nous en empêche, mais bien notre propre jugeote. Ici c’est pareil, il suffit d’avoir 2 neurones connectés pour ne pas faire l’erreur. Et d’ailleurs si tu prend la catégorie et que juste après tu la met à 0, le compilo ne va pas non plus capturer l’erreur, alors que c’est faux.
                Du coup pour répondre à la question maintenant que j’ai un peu plus lu le post, pourquoi ne pas cacher ces manipulations """dangereuses""" dans une classe pour faire en sorte que l’extérieur ne puisse pas faire des ++ et compagnie ?
                • Partager sur Facebook
                • Partager sur Twitter
                  2 novembre 2021 à 15:35:36

                  > Pourquoi donc voudrais tu avoir une énumération "simple" au lieu d'une énumération fortement typée ?

                  Pour des champs de bits, ça me laisse assez dubitatif. Car si on donne un nom à chaque bit, on ne le fait plus vraiment sur les compositions intermédiaires. Il faut rajouter beaucoup d'huile de coude je trouve. Un peu comme quand on définit des entiers fortement typés avec `enum class line_t : int_resolution_qui_va_bien_t {};` + la surcharge de toute l'arithmétique.

                  C'est faisable, mais je trouve ça un peu lourdingue, et moins naturel que pour avoir des entiers fortement typés (à cause des trous anonymes). L'avantage du vieil enum pour définir des champs de bits, c'est qu'après tout est défini (en matière d'opérateurs binaires bit à bit). Sans ça, autant utiliser un bitset, quitte à implémenter le notre pour réduire l'empreinte mémoire. Ca me parait plus DRY sur le long termes que de surcharger |, &, ^, etc sur chaque enum class qui correspond à un champ de bits que l'on utilisera dans nos projets.

                  • Partager sur Facebook
                  • Partager sur Twitter
                  C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                    2 novembre 2021 à 22:02:21

                    Pour ce genre d'enum, j'ai 2 composants:

                    • L'enum dont les valeurs correspondent à la position d'un bit
                    • Une classe générique qui définit tous les opérateurs avec elle-même et avec l'enum.

                    Le tout est reliée par un trait du genre

                    template<class E>
                    class enum_as_flag
                    {
                      static const std::size_t max = E::MAX_;
                    };

                    Trait qui sera spécialisé au besoin. Au final, on peut faire E::value1 | E::value2 qui retourne un Flags<E> avec le code ci-dessous:

                    template<class E, class = void>
                    struct is_enum_flag : std::false_type
                    {};
                    
                    template<class E>
                    struct is_enum_flag<E, decltype(void(enum_as_flag<E>::max))> : std::true_type
                    {};
                    
                    template<class E> constexpr std::enable_if_t<is_enum_flag<E>::value, Flags<E>>
                    operator|(E lhs, E rhs) noexcept { return Flags<E>(lhs) | rhs; }
                    • Partager sur Facebook
                    • Partager sur Twitter
                      7 novembre 2021 à 17:44:41

                      @lmgh:
                      J'ai fait des tests en implémentant les operateurs bit à bit (or et and), ta solution fonctionne plutôt bien.

                      Je n'ai pas vraiment réussi à utiliser la fonction get, je ne sais pas pourquoi.

                      Autre bizarrerie, avec cout, dès que j'utilise une expression, je doit utiliser des parenthèses, la encore, la raison m'échappe.

                      PS:
                      Les templates et moi, on est pas de grand copains.

                      Code:

                      #include <bitset>
                      #include <iostream>
                      #include <cassert>
                      
                      namespace Category
                      {
                          enum class Type
                          {
                              None,
                              Scene,
                              Player,
                              Allied,
                              Ennemy,
                              Max,
                              Error
                          };
                      }
                      
                      template <typename T>
                      constexpr size_t toIndex(T value) {
                          static_assert(std::is_enum<T>::value,
                              "Parameter type should be an enumeration");
                          return static_cast<size_t>(value);
                      }
                      
                      template <typename T>
                      struct CategoryTrait {
                          static_assert(std::is_enum_v<T>,
                              "Parameter type should be an enumeration");
                          using category = T;
                          using bit_array = std::bitset<toIndex(T::Max)>;
                          static constexpr bit_array& set(bit_array& array, T pos, bool value = true) {
                              array.set(toIndex(pos), value);
                              return array;
                          }
                          static constexpr bit_array& reset(bit_array& array, T pos) {
                              array.reset(toIndex(pos));
                              return array;
                          }
                          static constexpr bool get(bit_array& array, T pos) {
                              return array[toIndex(pos)];
                          }
                      };
                      
                      template <typename T,
                          typename trait = CategoryTrait<T>>
                          typename trait::bit_array& set(typename trait::bit_array& array, T pos, bool value = true) {
                          return trait::set(array, pos, value);
                      }
                      template <typename T,
                          typename trait = CategoryTrait<T>>
                          typename trait::bit_array& reset(typename trait::bit_array& array, T pos) {
                          return trait::reset(array, pos);
                      }
                      template <typename T,
                          typename trait = CategoryTrait<T>>
                          constexpr bool get(typename trait::bit_array& array, T pos) {
                          return trait::get(array, pos);
                      }
                      
                      using CatArray = CategoryTrait<Category::Type>::bit_array;
                      
                      void ManagmentRule(CatArray const& operand)
                      {
                          // Règle de gestion:
                          // Player, Allied et Ennemy sont mutuellement exclusifs
                      
                              // Ko
                          //if ((get(operand, Category::Type::PlayerAircraft)) == true) ||
                          //    (get(operand, Category::Type::AlliedAircraft)) == true) ||
                          //    (get(operand, Category::Type::EnnemyAircraft)) == true))
                          if ((operand.test(static_cast<std::size_t>(Category::Type::Player)) == true) ||
                              (operand.test(static_cast<std::size_t>(Category::Type::Allied)) == true) ||
                              (operand.test(static_cast<std::size_t>(Category::Type::Ennemy)) == true))
                              assert(operand.test(static_cast<std::size_t>(Category::Type::Player)) ^ operand.test(static_cast<std::size_t>(Category::Type::Allied)) ^ operand.test(static_cast<std::size_t>(Category::Type::Ennemy)));
                          
                      }
                      
                      CatArray operator|(Category::Type lft, Category::Type rgt)
                      {
                          CatArray left;
                          set(left, lft);
                          
                          CatArray right;
                          set(right, rgt);
                      
                          CatArray result{ left | right };
                          ManagmentRule(result);
                          return result;
                      }
                      
                      CatArray operator|(CatArray const& lft, Category::Type rgt)
                      {
                          CatArray right;
                          set(right, rgt);
                      
                          CatArray result{ lft | right };
                          ManagmentRule(result);
                          return result;
                      }
                      
                      CatArray operator|(Category::Type lft, CatArray const& rgt)
                      {
                          return rgt | lft;
                      }
                      
                      CatArray operator&(Category::Type lft, Category::Type rgt)
                      {
                          CatArray left;
                          set(left, lft);
                      
                          CatArray right;
                          set(right, rgt);
                      
                          return left & right;
                      }
                      
                      CatArray operator&(CatArray const& lft, Category::Type rgt)
                      {
                          CatArray right;
                          set(right, rgt);
                      
                          return lft & right;
                      }
                      
                      CatArray operator&(Category::Type lft, CatArray const& rgt)
                      {
                          return rgt & lft;
                      }
                      
                      bool operator==(CatArray const& lft, Category::Type rgt)
                      {
                          return lft.test(static_cast<std::size_t>(rgt));
                              // Ko
                          // return get(lft, rgt);
                      }
                      
                      bool operator==(Category::Type lft, CatArray const& rgt)
                      {
                          return rgt == lft;
                      }
                      
                      int main()
                      {
                          CatArray array;
                          set(array, Category::Type::Player);
                          std::cout << get(array, Category::Type::Player) << std::endl;
                              // lance une exception (j'aurais préféré une assertion, mais bon...)
                          //set(array, Category::Type::Error);
                              // IDEM
                          //std::cout << get(array, Category::Type::Error) << "\n"; 
                          std::cout << (Category::Type::Player | Category::Type::Scene) << std::endl;
                      
                          CatArray result{ Category::Type::Player | Category::Type::Scene };
                          std::cout << result << std::endl;
                          std::cout << (result & Category::Type::Scene) << std::endl;
                          std::cout << ((result & Category::Type::Scene) == Category::Type::Allied)  << std::endl;
                          std::cout << ((Category::Type::Player | Category::Type::Scene) == Category::Type::Allied) << std::endl;
                          std::cout << ((Category::Type::Player | Category::Type::Scene) == Category::Type::Ennemy) << std::endl;
                              // Lance une assertion (comme prévu)
                          //std::cout << (Category::Type::Player | Category::Type::Ennemy) << std::endl;
                      }

                      -
                      Edité par Deedolith 7 novembre 2021 à 17:45:41

                      • Partager sur Facebook
                      • Partager sur Twitter
                        8 novembre 2021 à 15:54:28

                        JadeSalina a écrit:

                        J’avais pas lu le post en entier. Tu es en train d’essayer de t’empecher de faire ++ sur la catégorie ?? Bah pourquoi le faire en premier lieu, si c’est pas ce que tu veux faire ?

                        Oh, simplement parce

                        • que le collègue qui travaille juste à coté de toi ne le sait pas, et qu'il risque d'essayer de le faire
                        • dans trois mois, toi aussi tu auras oublié que tu ne voulais pas le faire

                        JadeSalina a écrit:

                        À quel moment tu peux faire ce genre d’erreur (sauf bourré à 3h du mat) ?

                         Tu peux me croire sur parole, il n'y a pas que bourré à  trois heures du mat que toi (ou un de tes collègues) risquiez de le faire...  Ne serait-ce que parce que vous pensiez qu'il serait pas mal d'itérer sur les différentes catégories à l'une ou l'autre occasion ;)

                        JadeSalina a écrit:

                        D’ailleurs pourquoi mettre const puisque ça nous empêche pas de const_cast

                        Parce que, si tu  dois const_cast à un moment donné, c'est très certainement que tu as loupé quelque chose.

                        D'ailleurs, de manière générale, dés que  tu dois caster, c'est sans doute (sauf exceptions très rares) que tu as loupé quelque chose ;)

                        JadeSalina a écrit:

                        Ici c’est pareil, il suffit d’avoir 2 neurones connectés pour ne pas faire l’erreur.

                        Ah, tu  n'as jamais  vécu une situation dans laquelle tes deux seuls neurones utilisables faisaient la course?

                        Ne t'en fais pas, ca va arriver plus vite que tu ne le crois ;)

                        JadeSalina a écrit:

                        Du coup pour répondre à la question maintenant que j’ai un peu plus lu le post, pourquoi ne pas cacher ces manipulations """dangereuses""" dans une classe pour faire en sorte que l’extérieur ne puisse pas faire des ++ et compagnie ?

                        Peut etre parce qu'on a des possibilité bien plus intéressantes ;)

                        • Partager sur Facebook
                        • Partager sur Twitter
                        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                          8 novembre 2021 à 18:42:25

                          koala01 a écrit:

                          JadeSalina a écrit:

                          J’avais pas lu le post en entier. Tu es en train d’essayer de t’empecher de faire ++ sur la catégorie ?? Bah pourquoi le faire en premier lieu, si c’est pas ce que tu veux faire ?

                          Oh, simplement parce

                          • que le collègue qui travaille juste à coté de toi ne le sait pas, et qu'il risque d'essayer de le faire
                          • dans trois mois, toi aussi tu auras oublié que tu ne voulais pas le faire

                          JadeSalina a écrit:

                          À quel moment tu peux faire ce genre d’erreur (sauf bourré à 3h du mat) ?

                           Tu peux me croire sur parole, il n'y a pas que bourré à  trois heures du mat que toi (ou un de tes collègues) risquiez de le faire...  Ne serait-ce que parce que vous pensiez qu'il serait pas mal d'itérer sur les différentes catégories à l'une ou l'autre occasion ;)

                          JadeSalina a écrit:

                          D’ailleurs pourquoi mettre const puisque ça nous empêche pas de const_cast

                          Parce que, si tu  dois const_cast à un moment donné, c'est très certainement que tu as loupé quelque chose.

                          D'ailleurs, de manière générale, dés que  tu dois caster, c'est sans doute (sauf exceptions très rares) que tu as loupé quelque chose ;)

                          JadeSalina a écrit:

                          Ici c’est pareil, il suffit d’avoir 2 neurones connectés pour ne pas faire l’erreur.

                          Ah, tu  n'as jamais  vécu une situation dans laquelle tes deux seuls neurones utilisables faisaient la course?

                          Ne t'en fais pas, ca va arriver plus vite que tu ne le crois ;)

                          JadeSalina a écrit:

                          Du coup pour répondre à la question maintenant que j’ai un peu plus lu le post, pourquoi ne pas cacher ces manipulations """dangereuses""" dans une classe pour faire en sorte que l’extérieur ne puisse pas faire des ++ et compagnie ?

                          Peut etre parce qu'on a des possibilité bien plus intéressantes ;)

                          Si le collègue ou qui que ce soit a la moindre idée de ce qu'il fait, il n'y a aucun moyen qu'il se mette à faire des additions sur la catégorie. La catégorie, on la récupére, on fait des comparaisons avec, etc, à aucun moment on ne veut l'additionner. A ce niveau là, vous devriez craindre que le collègue se mette à faire des calculs puis renvoyer 0 au lieu de renvoyer le calcul ou des trucs comme ça. Je vous conseille de vous concentrer et de mettre votre énergie sur la résolution des problèmes eux même au lieu d'essayer d'empêcher des choses qui n'existent pas. Et si vous même au bout de 3 mois vous oubliez ce que vous faites mais ou va le monde, dans ce cas il faut vous rapprocher d'un hopital psychiatrique pour éviter que la démence ne dégénère.

                          Et pour l'instant sur ce forum je ne vois que des gens qui parlent de manière théorique sur les "bonnes pratiques" etc, mais j'aimerais voir ce que vous codez au quotidien et ce que vous arrivez à faire en pratique

                          -
                          Edité par JadeSalina 8 novembre 2021 à 18:43:33

                          • Partager sur Facebook
                          • Partager sur Twitter
                            8 novembre 2021 à 19:12:44

                            Toute histoire de bug commence par quelqu'un qui dit "on ne fera pas cette erreur, pas besoin de s'en occuper" :)

                            En tout cas, merci du diagnostic, j'envoie un email immediatement a tous ceux qui ont besoin de consulter dans cette liste https://en.wikipedia.org/wiki/List_of_software_bugs

                            JadeSalina a écrit:

                            mais j'aimerais voir ce que vous codez au quotidien et ce que vous arrivez à faire en pratique

                            Je me pose aussi la question pour toi.

                            De mon coté, bossant sur des projets de plus de 1 million de ligne de code et des centaines de devs, je dois reconnaître que je n'arrive pas à savoir si tout le monde fait parfaitement son boulot et qu'aucune erreur se glisse dans les codes.

                            A vrai dire, même dans mes propres codes, il m'arrive de faire des erreurs. Je retiens l'astuce de l'hôpital psy et je te promets que je vais aller consulter pour diagnostiquer une éventuelle démence de ma part, mais l'explication la plus probable reste que les humains font des erreurs. Et que penser son code pour qu'il soit résistant aux erreurs n'est pas une approche plus mauvaise que de passer des heures, des jours ou des semaines, à identifier et corriger un bug.

                            Et les humains font aussi parfois preuve d'arrogance. (Ce qui est aussi une source bien connue d'erreurs)

                            -
                            Edité par gbdivers 8 novembre 2021 à 19:15:21

                            • Partager sur Facebook
                            • Partager sur Twitter

                            Rejoignez le discord NaN pour discuter programmation.

                              8 novembre 2021 à 19:29:57

                              JadeSalina, on peut connaitre un bout de ton CV pour que tu sois si péremptoire ?

                              Près de 25 ans dans le dev (carte à puces, banques d'investissement, logiciel "base satellite", encodage vidéo, guerre électronique, network game code, ...), c'est vrai que cela ne fait pas de moi un "vrai" dev, mais bon, on fait ce qu'on peut, et toi ?

                              • Partager sur Facebook
                              • Partager sur Twitter
                              Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                8 novembre 2021 à 19:46:00

                                bacelar a écrit:

                                JadeSalina, on peut connaitre un bout de ton CV pour que tu sois si péremptoire ?

                                Près de 25 ans dans le dev (carte à puces, banques d'investissement, logiciel "base satellite", encodage vidéo, guerre électronique, network game code, ...), c'est vrai que cela ne fait pas de moi un "vrai" dev, mais bon, on fait ce qu'on peut, et toi ?


                                Je n'ai pas dit que vous étiez mauvais, là vous me donnez des exemples mais je ne vois pas votre code, combien de lignes vous tapez par semaine, etc. Après j'ai aussi vu des gens qui ont +30 ans d'expérience et qui savent tout juste faire des choses basiques (par ex David de Gamecodeur) et même moi qui n'ai pas beaucoup d'expérience je sait faire des choses plus évoluées
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  8 novembre 2021 à 21:57:28

                                  Peut-être mais tu manques cruellement de recul.

                                  Si ce n'est pas toi qui te plantes, ce sera ton collègue.
                                  Si ce n'est pas ton collègue, ce sera son successeur, ou le successeur de son successeur, ou le stagiaire du successeur (bref, quelqu'un dont tu ne connais ni l'identité, ni le niveau).

                                  Et le premier moyen de se prémunir de bugs, est de s'appuyer sur le compilateur.
                                  Plus il gueule, mieux on se porte.

                                  enum WeakType
                                  {
                                      None,
                                      Scene,
                                      Player,
                                      Allied,
                                      Ennemy
                                  }
                                  
                                  enum class StrongType
                                  {
                                      None,
                                      Scene,
                                      Player,
                                      Allied,
                                      Ennemy
                                  }
                                  
                                  int main()
                                  {
                                      auto weak = WeakType::Scene + 1;
                                                    // Le compilateur ne dit rien (convertion implicite), 
                                                    // bonne chance pour trouver une connerie comme celle la 
                                                    // noyée au milieux de centaines de milliers de lignes de code.
                                  
                                      auto strong = Strongtype::Scene + 1;    // Le compilateur gueule
                                                                              // c'est facilement identifiable. 
                                  }

                                  Et cela sans parler du coût que cette connerie qui te parait anodine peut engendrer.
                                  Tips: 1 jour/homme de développement, c'est environ 400€

                                  -
                                  Edité par Deedolith 8 novembre 2021 à 22:40:25

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    8 novembre 2021 à 23:48:28

                                    Je ne comprends pas... Il existe bien des projets en C, ça veut pas dire qu'ils sont inmaintenables, fondamentalement on peut se passer de tout ça et quand même s'en sortir
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      9 novembre 2021 à 0:34:15

                                      "s'en sortir" n'est pas suffisant dans le développement moderne. Parce que l'histoire de l'informatique et nos expériences professionnelles nous ont appris que des mauvaises pratiques ont des coûts importants de développement et de maintenance et peuvent provoquer des problèmes critiques. Et que les problèmes vont être de pire en pire avec l'augmentation de la complexité des applications. Et que la meilleure approche, c'est que les langages de programmation et les outils de développement interdisent de faire des erreurs, plutôt que d'espérer que les humains n'en fassent pas.
                                      • Partager sur Facebook
                                      • Partager sur Twitter

                                      Rejoignez le discord NaN pour discuter programmation.

                                        9 novembre 2021 à 9:55:22

                                        @Deedolith : Tips: 1 jour/homme de développement, c'est environ 400€

                                        Chez nous c'est 660 Hors taxes jour ... 

                                        -
                                        Edité par ManOfStyle 9 novembre 2021 à 9:57:58

                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        Enumerations et operations bit à bit.

                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                        • Editeur
                                        • Markdown