• # l'article est intéressants

    Posté par  . Évalué à 4.

    mais les commentaires au moment ou je le lis sont affligeant. Outre ceux qui disent que c'est une mauvaise idée sans dire pourquoi, d'autre qui disent qu'il faudrait plutôt passer a Rust sans expliquer pourquoi l'analyse de l'auteur n'est pas bonne…

    ou celui qui n'a pas l'air d'avoir remarque que depuis Windows ME, la qualité de Fenêtre s'est grandement amélioré…

    Bon je je vais pas tergiverser, je sais bien pourquoi je suis ici :D

    Le point clé est souligné par l'auteur, il faut se contenter d'un sous ensemble de c++, chose qui a l'air d'avoir été éludé par les commentateur dev C++ professionnels. Je trouve personnellement que le c++ ajoute des fonctionnalité manquante au C, comme les destructeurs; ou les templates qui offrent une bonne sécurité et de bonne capacité de spécialisation en fonction des types; pour faire la même chose avec des macros, faut relire 10 fois avant de s'assurer qu'on a bien fait taff.

    L'autre énorme avantage de c++ c'est que la base de code principale peu dans son immense majorité être reprise telle quelle. Par contre… bonjour les tests de non régression…

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

    • [^] # Re: l'article est intéressants

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

      les templates qui offrent une bonne sécurité et de bonne capacité de spécialisation en fonction des types; pour faire la même chose avec des macros, faut relire 10 fois avant de s'assurer qu'on a bien fait taff.

      #define cmp(a, b) _Generic(b, int : cmpInt, float: cmpFloat, default: cmpPtr)(a,b)
      

      Tu te relis beaucoup de fois pour un truck qui se fait en une ligne de code, et est assez explicite, je trouve.

      Bon ok, c'est du C11, et si tu veux faire la même chose avec des macros en C++, ça va peu être plus compliqué.

      • [^] # Re: l'article est intéressants

        Posté par  . Évalué à 4.

        Tu te relis beaucoup de fois pour un truck qui se fait en une ligne de code, et est assez explicite, je trouve.

        que fait cmp(1.2f, 2) et cmp(2, 1.2f); on appel pas la même comparaison. même question si je met des doubles, on passe dans la comparaison de pointeur, si je met des short aussi, en c++ y'a une belle erreur de compilation qui dit qu'il y'a trop de candidats.

        j’ajouterais que 1.0 c'est double donc cmp (2.0, 1.0) appel la comparaison de pointeur

        si tu veux faire la même chose avec des macros en C++, ça va peu être plus compliqué.

        en c++ on va préférer les templates et/ou surcharge

        disons que la macros devient inutile, seul le template reste et tu peux lui ajouter une contrainte sur le type; tu peux faire une version full template en spécialisant plutôt que faire la surcharge (l'un des soucis du c++ et les 15 façons de faire); bonus avec la version full template tu peux forcer celle que tu veux appeler cmp(a,b)

        au final si tu veux rester homogène :

        template <typename T>
        int cmp(T a, T b) {
          static_assert(std::is_pointer<T>::value, "les paramètres ne sont pas des pointeurs");
          std::cout <<" ptr " ;
          return a-b;
        }
        
        template <> int cmp(int a, int b) { 
          std::cout <<" int " ;
          return a-b;
        }
        
        template <> int cmp(float a, float b) {
          std::cout <<" float " ;
          return (int)(a-b);
        }
        
        template <> int cmp(double a, double b) {
          std::cout <<" double " ;
          return (int)(a-b);
        }
        
        int main() {
          short a=1, b=2;
          std::cout << cmp(1,2) << std::endl;
          std::cout << cmp<int>(1,2.0) << std::endl;
          std::cout << cmp<double>(1.0,2) << std::endl;
          std::cout << cmp(1.0f,2.0f) << std::endl;
          std::cout << cmp(a,b) << std::endl;
        
          return 0;
        }

        bon c'est vite fait et manque les long long et un paquet d'autre types; il vaut mieux utiliser les std::is_floating_point ou std::is_integral; tu peux regretter que c'est plus long que ton oneliner, mais en fait même pas vu qu'il faut que tu définisse quand même tes cmpInt, cmpFloat et cmpPtr, et on ajoute de la souplesse à l'utilisation, et on peut même l'étendre au besoin en rajoutant un type. A noter qu'on a une erreur de compilation sur cmp(a,b) car short != int, donc on passe dans le cas par défaut.

        L'erreur est explicite :

        file.cpp: In instantiation of ‘int cmp(T, T) [with T = short int]’:
        file.cpp:31:23:   required from here
        file.cpp:5:37: error: static assertion failed: les paramètres ne sont pas des pointeurs
            5 |   static_assert(std::is_pointer<T>::value, "les paramètres ne sont pas des pointeurs");
        
         In instantiation of ‘int cmp(T, T) [with T = short int]’:
        
        

        Bref je ne sais pas si ça suffit a convaincre, mais je trouve la version macro vachement plus risquée.

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

        • [^] # Re: l'article est intéressants

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

          En C, t'a une erreur aussi (Vu que 1.0 est un double, il va dans le type default, donc cmpPtr)

              #include <stdio.h>
          
              #define cmp(a, b) _Generic(b, int : cmpInt, float: cmpFloat, default: cmpPtr)(a,b)
          
              int cmpPtr(void *a, void*b) {return a == b;}
              int cmpInt(int a, int b) {return a == b;}
              int cmpFloat(float a, float b) {return a == b;}
              int main()
              {
                  printf("Hello, World! %d\n", cmp(1,1.0));
              }
          Main.c: In function ‘main’:
          Main.c:10:38: warning: passing argument 1 of ‘cmpPtr’ makes pointer from integer without a cast [-Wint-conversion]
             10 |     printf("Hello, World! %d\n", cmp(1,1.0));
                |                                      ^
                |                                      |
                |                                      int
          Main.c:3:79: note: in definition of macro ‘cmp’
              3 | #define cmp(a, b) _Generic(b, int : cmpInt, float: cmpFloat, default: cmpPtr)(a,b)
                |                                                                               ^
          Main.c:5:18: note: expected ‘void *’ but argument is of type ‘int’
              5 | int cmpPtr(void *a, void*b) {return a == b;}
                |            ~~~~~~^
          Main.c:10:40: error: incompatible type for argument 2 of ‘cmpPtr’
             10 |     printf("Hello, World! %d\n", cmp(1,1.0));
                |                                        ^~~
                |                                        |
                |                                        double
          Main.c:3:81: note: in definition of macro ‘cmp’
              3 | #define cmp(a, b) _Generic(b, int : cmpInt, float: cmpFloat, default: cmpPtr)(a,b)
                |                                                                                 ^
          Main.c:5:26: note: expected ‘void *’ but argument is of type ‘double’
              5 | int cmpPtr(void *a, void*b) {return a == b;}
                |                     ~~~~~^
          
          

          Si tu enlèves le default t'a aussi une erreur :

          Main.c: In function ‘main’:
          Main.c:10:40: error: ‘_Generic’ selector of type ‘double’ is not compatible with any association
             10 |     printf("Hello, World! %d\n", cmp(1,1.0));
                |                                        ^~~
          Main.c:3:28: note: in definition of macro ‘cmp’
              3 | #define cmp(a, b) _Generic(b, int : cmpInt, float: cmpFloat)(a,b)
                |    
          

          Ce que fait cmp, c'est juste appeler une fonction qui dépend du type de b (doc ici: https://www.iso-9899.info/n1570.html#6.5.1.1).
          Après si l'appelle de fonction déroulée pas la macro n'est pas bon, le compilateur va le remarquer.
          Dalleur utiliser default n'est généralement pas une super idée.

          si tu veux faire la même chose avec des macros en C++, ça va peu être plus compliqué.

          Cette remarque n'était pas pour dire que _Generic > template, mais que si tu essayes de reproduire le comportement des template C++ en macro C++, ça va effectivement être compliqué, et hasardeux.

    • [^] # Re: l'article est intéressants

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

      Bon les destructeurs, je suis d'accord que ça manque au C,

      Mais gnu C, implémente une extension __atribute__((cleanup())), qui est implémenté par gcc, clang, tinycc, intel compiler, et est utilisé dans flatpak, systemd, et libvirt pour ne citer que quelques exemples.

    • [^] # Re: l'article est intéressants

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

      L'autre énorme avantage de c++ c'est que la base de code principale peu dans son immense majorité être reprise telle quelle. Par contre… bonjour les tests de non régression…

      C'est un piège, plus les versions de C et de C++ avancent, plus il y a des différences subtiles entre les deux et il faut faire attention.

      La base de code du noyau est grande, des subtilités bas niveau s'y glissent en plus des subtilités liées à chaque architecture ou du modèle mémoire. Cela n'est pas trivial à introduire sans risquer de casser quelque chose.

      L'autre problème c'est maintenir le code. Qui va relire ou maintenir le code C++ ? Les développeurs actuels maitrisent le C, mais pas forcément le C++. Et non le C++ ce n'est pas juste du C en mieux, ça reste un langage très distinct qui nécessite une expertise comme pour le C si on veut éviter les conneries.

      Et alors que Rust est introduit, ajouter un 3e langage majeur dans le code est-ce vraiment soutenable en tenant compte de ces critères ? Rust montre bien que l'exercice n'est pas trivial même si à priori la barrière pour le C++ est moins haute.

      • [^] # Re: l'article est intéressants

        Posté par  . Évalué à 2.

        Je pense que l'approche Rust est de prendre un langage qui :
        - est très différent, donc pas de confusion possible, pas de mélange des genres.
        - apporte une réelle plus value pour les besoins du noyau.

        Le C++ a contrario expose au risque de croire que c'est quasiment comme du C et donc de faire des erreurs grossières tout en apportant pas de gain majeur sur ce qui compte vraiment.

Suivre le flux des commentaires

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