Journal Le bon sens et le C++

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
6
14
nov.
2024

Demat' iNal.

On a l'impression, parfois, que dans un code compilé, pour savoir si une fonction (locale) est utilisée, il suffit de la supprimer, recompiler et si on obtient une erreur, elle était utilisée. Question de bon sens ! D'ailleurs c'est le cas en C [*]

Bien entendu, c'est faux, comme le montre le code C++ suivant:

#include <cstdio>

#ifndef REMOVE
static void foo(int x) {
    puts("foo(int)");
}
#endif

static void foo(float y) {
    puts("foo(float)");
}

int main() {
    foo(1);
}

Si REMOVE est défini, le code continue à compiler mais a un comportement différent à l'exécution. On peut forger des exemples similaires avec les conversions implicites.

Comme quoi

Le bon sens n'est rien d'autre qu'un ensemble de préjugés qui reposent dans votre esprit avant que vous n'ayez eu 18 ans — (Einstein semble-t-il)

se transpose bien à l'informatique :-)

[*] je pense !

  • # bah non

    Posté par  . Évalué à 2 (+1/-2). Dernière modification le 14 novembre 2024 à 13:15.

    Et ça ne marche pas non plus en java (je dirai même plus c'est pire), et encore moins en python !

    grep et consort restent des outils bien plus fiable pour s'assurer d'éviter de faire des boulettes, les IDE peuvent aussi donner des indications, mais une absence de résultat n'indique pas forcément une absence d'utilisation.

    ensuite si la fonction est locale un find in file sera bien plus efficace qu'une compilation.

    Et globalement tous les langages permettant introspection ne peuvent reposer sur une méthode aussi basique.

    Dison qu'on a pas le même bon sens.

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

    • [^] # Re: bah non

      Posté par  (site web personnel) . Évalué à 3 (+0/-0).

      Sinon il y a des outils dédiés type cppcheck qui peuvent faire une telle évaluation et faire un beau rapport à la fin.

      Cela évite ce genre de pièges même si cela n'est jamais parfait.

      • [^] # Re: bah non

        Posté par  . Évalué à 3 (+0/-0).

        Perso je me base surtout de la détection de code mort de l'ide qui doit être une interface ou un réimplémentation de cppcheck. Mais il lui arrive de rater des trucs, notamment a cause de certains template ou de #ifdef / #ifndef.

        grep donne un résultat qu'il faut regarder ensuite, mais sur du c++ il ne m'a jamais mis en défaut, comprendre par la des faux positifs; mais jamais une non détection.

        Sur le java c'est encore plus compliqué, car avec l’introspection les auto discover et autre y'a pas de solution fiable à 100% (ne surtout pas se fier à grep), encore qu'avec les dernières versions de java il me semble qu'on peut plus accéder aux private via introspection.

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

  • # Conversion implicite

    Posté par  (site web personnel) . Évalué à 3 (+1/-0). Dernière modification le 14 novembre 2024 à 13:16.

    Tu triches avec les conversions implicites…

    Allez :

    template <class T> void foo(T) = delete;
    

    Avant ton main et c'est bon.

    Ou la totale en C++20:

    template <class T> requires std::same_as(T, float) void foo(T x) {
        puts("foo(float)");
    }
    
  • # Beurk les conversions implicites

    Posté par  (site web personnel) . Évalué à 2 (+0/-0).

    Imaginez une API qui permet de sérialiser des données binaires en fonction du type en entrée :

    #include <iostream>
    #include <cstdint>
    
    void pack(uint8_t x)  { puts("uint8_t");  }
    void pack(uint16_t x) { puts("uint16_t"); }
    void pack(uint32_t x) { puts("uint32_t"); }
    void pack(uint64_t x) { puts("uint64_t"); }
    void pack(int8_t x)   { puts("int8_t");   }
    void pack(int16_t x)  { puts("int16_t");  }
    void pack(int32_t x)  { puts("int32_t");  }
    void pack(int64_t x)  { puts("int64_t");  }
    void pack(float x)    { puts("float");    }
    
    int main() {
        pack(1);
        return 0;
    }

    Résultat possible :

    $ ./a.out
    int32_t
    

    Du coup, si on veut sérialiser des entiers spécifiques à la suite on doit forcer un cast.

    pack(static_cast<uint8_t>(10));
    pack(static_cast<uint16_t>(1664));

    Perso je vote pour des spécialisations de template ou des noms explicites (comme packu64 mais moins C++/template/metaprogramming friendly)

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

Envoyer un commentaire

Suivre le flux des commentaires

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