1. #1
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 023
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 023
    Points : 4 423
    Points
    4 423

    Par défaut Plusieurs versions d'un bout de code

    Bonjour ,

    Je me demandais comment on pouvait copié/ collé un bout de code
    Précision: quel compilateur? Je dirais C++03

    Un exemple: Prenons l'exemple de l'algo de Morris, Joseph H. pour traverser un arbre de façon itérative
    Et je voudrais:
    1. Une version brute/ optimale -> la version originale
    2. À partir de la version originale, rajouté des logs
    3. À partir de la version originale, rajouté des traces pour le débogage



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    iterativeInorder(node)
      s <- empty stack
      while (not s.isEmpty() or node != null)
        if (node != null)
          s.push(node) // action
          node <- node.left
        else
          node <- s.pop()
          visit(node)
          node <- node.right
     
     
    iterativeInorder_log(node)
      s <- empty stack
      while (not s.isEmpty() or node != null)
        if (node != null)
          s.push(node) // action
          display(node) // log
          node <- node.left
        else
          node <- s.pop()
          visit(node)
          node <- node.right
     
     
    iterativeInorder_debug(node)
      s <- empty stack
      while (not s.isEmpty() or node != null)
        if (node != null)
          s.push(node) // action
          display("Here node " + node) // debug
          node <- node.left
          display("Go left, node " + node) // debug
        else
          node <- s.pop()
          display("Pop node " + node) // debug
          visit(node)
          node <- node.right
          display("Go right, node " + node) // debug

    Une solution "template" existe, mais cela nécessite
    • De tout mettre dans les entêtes. Et pour un algo de XXX lignes avec XXX entêtes, c'est moche
    • D'avoir des alertes parce que le compilateur détecte du "code mort" (*). Et en espérant que le compilateur/ linker le supprime (avec les tests), sinon on perd un peu d'optimal avec tout un tas de tests inutiles


    *, édit: plus exactement, des alertes "expression constante". Et donc, un if (false) { ... } produit du "code mort"


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    tempate<bool with_debug, bool with_log>
    iterativeInorder(node)
      s <- empty stack
      while (not s.isEmpty() or node != null)
        if (node != null)
          s.push(node) // action
          if (with_log) { display(node) }
          if (with_debug) { display("Here node " + node) }
          node <- node.left
          if (with_debug) { display("Go left, node " + node) }
        else
          node <- s.pop()
          if (with_debug) { display("Pop node " + node) }
          visit(node)
          node <- node.right
          if (with_debug) { display("Go right, node " + node) }
     
     
    version originale: iterativeInorder<false, false> (...)
    version log:  iterativeInorder<false, true> (...)
    version debug:  iterativeInorder<true, false> (...)

  2. #2
    Membre expérimenté
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    avril 2016
    Messages
    365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : avril 2016
    Messages : 365
    Points : 1 590
    Points
    1 590

    Par défaut

    Bonjour,

    Et avec des macros ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #ifdef WITH_LOG
    #define DISPLAY_IF_LOG(x) display(x)
    #else
    #define DISPLAY_IF_LOG(x)
    #endif
     
    #ifdef WITH_DEBUG
    #define DISPLAY_IF_DEBUG(x) display(x)
    #else
    #define DISPLAY_IF_DEBUG(x)
    #endif

  3. #3
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 023
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 023
    Points : 4 423
    Points
    4 423

    Par défaut

    Citation Envoyé par Pyramidev Voir le message
    Et avec des macros ?
    Je pense à une solution avec des macros mais le problème n'est pas le code que tu rajoutes (*), mais le code de l'algo que l'on doit pouvoir modifier facilement.

    Regarde mon exemple (sans question désolé ) tu as 3 fonctions iterativeInorder, iterativeInorder_log, iterativeInorder_debug que le développeur peut utiliser l'une ou l'autre en fonction du contexte (**)
    Mais ce sont les mêmes en définitive ... aux rajouts près.

    Maintenant remplace cet algo par un algo complexe de X000 de lignes, plein de boucles, ...

    Une solution avec un copié/ collé manuel va demander un effort de vérification

    Une autre solution consiste à n'avoir qu'1 seule fonction mais à la surcharger de tests+booléens pour avoir les différentes versions.
    Mais l'algo va "prendre des lignes" avec des "fonctionnalités" qui peuvent être inutiles en fonction du contexte (debug vs production, option activée ou pas, ...)


    * -> et effectivement, qu'on peut facilement activer/ désactiver avec des macros

    ** -> Le but aussi c'est d'éviter de recompiler le programme pour avoir une version ou une autre version. On peut, par exemple, avoir la version débogue cachée ... mais utilisable à tout instant. Ceci, c'est la limite des macros comme tu les utilises

  4. #4
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2011
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2011
    Messages : 476
    Points : 1 996
    Points
    1 996

    Par défaut

    Les points négatifs pour la solution template n'en sont pas vraiment:
    - Le .h peut définir 3 prototypes et le .cpp utiliser une template présente uniquement dans le cpp.
    - Les templates sont remplit de code mort, c'est prévu pour et sans warnings.

    Par contre, je ferrais un petit wrapper sur display histoire de ne pas faire les condition et avoir quelque chose de plus conviviable.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template<bool x> struct Display
    { 
      template<class... Ts>
      void operator()(Ts && ... args) {
        if(x) { display(args + ...); } // syntaxe c++17, on peut envisager des prototypes pour 1,2,3,etc paramètres en c++03
      };
    };
     
    Display<with_log> log;
    Display<with_debug> debug;
    //...
    log(node);
    debug("Here node ", node); // pas de concaténation pour éviter les opérations inutiles
    .

  5. #5
    Membre émérite
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    décembre 2015
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : décembre 2015
    Messages : 478
    Points : 2 263
    Points
    2 263

    Par défaut

    Citation Envoyé par foetus Voir le message
    Une solution "template" existe, mais cela nécessite
    • De tout mettre dans les entêtes. Et pour un algo de XXX lignes avec XXX entêtes, c'est moche
    • D'avoir des alertes parce que le compilateur détecte du "code mort". Et en espérant que le compilateur/ linker le supprime (avec les tests), sinon on perd un peu d'optimal avec tout un tas de tests inutiles
    Pour disposer simultanément des alternatives le template n'est pas si mal.
    On peut tout à fait mettre le code d'un template dans un fichier cpp (surtout dans ce cas où les valeurs des paramètres sont limitées). Il faudra seulement forcer les instanciations souhaitées de ce template dans ce fichier cpp (les non utilisées disparaîtront à l'édition des liens.)
    Pour éviter les alertes, il y a depuis le C++17 l'instruction if constexpr() qui répond a ton besoin. Mais aucun compilateur n'oubliera de supprimer en release un morceau de code sous un if (false).

  6. #6
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 023
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 023
    Points : 4 423
    Points
    4 423

    Par défaut

    Donc pour l'instant, je vois 2 solutions

    Solution 1: Le template qu'on appelle/ code dans 1 .cpp.
    Les inconvénients:
    1) Cela crée des fonctions/ méthodes avec 1 seul appel. Je pense qu'en poussant le compilateur, les appels de ces fonctions/ méthodes "'passthrough" vont être supprimés
    1 - bis) Ou alors coder autrement, mais mon code ne nécessite pas autant de réflexion
    2) Les alertes "expression constante"
    3) (voir après **) Ne pas avoir des signatures différentes. Par exemple, si je créé une interface Logger, il faudra 1) avoir un paramètre 2) l'interface complète

    Exemple succinct:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    template<bool with_log, bool with_debug>
    void real_test() {
    	printf("Line 1\n");
     
    	if (with_log) { printf("Line log\n"); }
     
    	if (with_debug) { printf("Line debug\n"); }
     
    	printf("Line 2\n");
    }
     
     
    void test_01() {
    	real_test<false, false> ();
    }
     
     
    void test_02() {
    	real_test<false, true> ();
    }
     
     
    void test_03() {
    	real_test<true, false> ();
    }

    Solution 2: Copié/ Collé + macros
    L’inconvénient c'est la maintenance ... même si on copie/ colle sans changements

    **: L'avantage, c'est qu'avec les macros, on cache des parties du code.
    Donc si je créé une interface Logger ou je ne sais pas quoi, on peut quand même coder mon algo avec cette interface.
    Et c'est seulement à l'activation de ces parties de code, qu'on passera les variables nécessaires.

    Exemple succinct:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    void test_01() {
    	printf("Line 1\n");
     
    #ifdef WITH_LOG
    	logger.log("Line log\n");
    #endif
     
    #ifdef WITH_DEBUG
    	printf("Line debug\n");
    #endif
     
    	printf("Line 2\n");
     
    #undef WITH_DEBUG
    #undef WITH_LOG
    }
     
     
    void test_02() {
    #define WITH_DEBUG 1
     
    	printf("Line 1\n");
     
    #ifdef WITH_LOG
    	logger.log("Line log\n");
    #endif
     
    #ifdef WITH_DEBUG
    	printf("Line debug\n");
    #endif
     
    	printf("Line 2\n");
     
    #undef WITH_DEBUG
    #undef WITH_LOG
    }
     
     
    void test_03(Logger& logger) {
    #define WITH_LOG 1
     
    	printf("Line 1\n");
     
    #ifdef WITH_LOG
    	logger.log("Line log\n");
    #endif
     
    #ifdef WITH_DEBUG
    	printf("Line debug\n");
    #endif
     
    	printf("Line 2\n");
     
    #undef WITH_DEBUG
    #undef WITH_LOG
    }

  7. #7
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    4 349
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 4 349
    Points : 17 040
    Points
    17 040

    Par défaut

    C'est le genre de truc qui se fait par macro.
    Tu mets les logs tout le temps, et une macro les active ou non. Ou à un certain niveau si le logger est plus qu'un simple printf et gère différents niveau de logs (verbose en debug, log en normal, rien en release, etc)
    Si t'es autant anti-macro que ça, tu peux mettre un flag enum en paramètre template.

  8. #8
    Membre averti

    Profil pro
    Inscrit en
    décembre 2013
    Messages
    298
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : décembre 2013
    Messages : 298
    Points : 307
    Points
    307

    Par défaut

    Citation Envoyé par foetus Voir le message
    Solution 2: Copié/ Collé + macros
    L’inconvénient c'est la maintenance ... même si on copie/ colle sans changements
    Cf le code de pyramidev, il propose d'utiliser une macro pour le display, pas utiliser les #ifdef directement dans l'algo (ce qui pour le coup alourdi effectivement la lecture).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void test_01() {
    	printf("Line 1\n");
     
    	LOG("Line log\n");
    	DEBUG("Line debug\n");
     
    	printf("Line 2\n");
    }
    Et tu peux (devrais) éviter le copier-coller avec les macros, en incluant plusieurs fois le meme fichier en changeant les macros.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // algo_impl.cpp
     
    #define CONCAT(x, y) x ## y
    #define FUNC_NAME(x, y) CONCAT(x, y)
     
    void FUNC_NAME(f, FUNC_SUFFIX) () {
    	printf("Line 1\n");
     
    	LOG("Line log\n");
    	DEBUG("Line debug\n");
     
    	printf("Line 2\n");
    }
     
    // algo.cpp
    #undef WITH_LOG
    #undef WITH_DEBUG
     
    #define FUNC_SUFFIX
    #include "algo_impl.cpp"
     
    #define WITH_LOG
    #define FUNC_SUFFIX _LOG
    #include "algo_impl.cpp"
     
    #define WITH_DEBUG
    #define FUNC_SUFFIX _DEBUG
    #include "algo_impl.cpp"
     
    f();
    f_LOG();
    f_DEBUG();
    On en revient alors a un probleme de macro vs template. Il n'y a pas besoin de type checking pour des logs, donc des macros peuvent être acceptables.

    EDIT : apres reflexion, je me dis que peut etre generer un nom de fonction comme ca directement avec une macro n'est pas simple/possible. A tester pour voir si je dis des betises... EDIT2 : cela fonctionne, en ajoutant des macros intermediaires (comme souvent avec les macros... c'est moche)

  9. #9
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 023
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : juillet 2013
    Messages : 2 023
    Points : 4 423
    Points
    4 423

    Par défaut

    Citation Envoyé par mintho carmo Voir le message
    Cf le code de pyramidev, il propose d'utiliser une macro pour le display, pas utiliser les #ifdef directement dans l'algo (ce qui pour le coup alourdi effectivement la lecture).
    Oui vous avez raison mais ce que j'avais en tête c'est d'activer/ désactiver un bout de code et non seulement 1 seule ligne.
    Parce que le tout macro c'est quand même chiant

    Par exemple, pour un logger réseau ou disque dur, on prépare le "job" à fournir au "thread pool" (<- mon implémentation)
    Ensuite on peut quand même avoir 1 seule ligne, en codant tout dans une classe. Seulement si on le peut
    C'est d'ailleurs mon idée d'interface: un logger qui peut être simplement un affichage écran, ou alors un logger fichier ou un réseau.

    Édit: On peut faire un mix des 2: #define pour quelques lignes et #ifdef pour un bloc
    En ayant plusieurs define (version macro)/ booléens (version template) pour avoir 1 seule version.
    Il faut juste les activer/ désactiver tous en même temps ... éventuellement le faire via du code si c'est possible


    Citation Envoyé par mintho carmo Voir le message
    On en revient alors a un probleme de macro vs template. Il n'y a pas besoin de type checking pour des logs, donc des macros peuvent être acceptables.
    C'est ce que je disais dans ce message et celui ci : c'est acceptable parce que pas d’allocations et pas de destructions.


    Citation Envoyé par mintho carmo Voir le message
    EDIT : apres reflexion, je me dis que peut etre generer un nom de fonction comme ca directement avec une macro n'est pas simple/possible. A tester pour voir si je dis des betises... EDIT2 : cela fonctionne, en ajoutant des macros intermediaires (comme souvent avec les macros... c'est moche)
    Le problème ce n'est pas le nom mais la signature .
    Comment tu fais pour passer l'interface logger (par exemple), mais seulement à une seule version ?

    Édit: C'est le même problème qu'avec la solution template. Mais dans ce cas, tu transformes ta fonction template en foncteur template et tous les paramètres sont mis en attribut "weak pointer" (et c'est la fonction "passthrough" qui donnent les bons paramètres au foncteur)

    Réponse: Dans ce cas, la seule façon c'est d'avoir un générateur de code en entrée le code et les signatures, en sortie X fichier .cpp contenant le code avec la bonne signature (et éventuellement l'entête).
    Ensuite, avec un générateur de code on peut aller plus loin.

    Mais sinon, l'idée de bourrin de faire des include en nombres est intéressante

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur systèmes embarqués
    Inscrit en
    juin 2009
    Messages
    3 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur systèmes embarqués

    Informations forums :
    Inscription : juin 2009
    Messages : 3 389
    Points : 8 857
    Points
    8 857
    Billets dans le blog
    1

    Par défaut

    Jamais un jour fait quelque chose similaire dans du code C. Je devais gérer l'intégration d'une stack IP avec un framework Java sur micro-contrôleur, et j'avais plein de traces qui étaient activables par macro. En souhaitant réécrire un bout de code semblable, je me suis rendu compte que ce n'était pas une bonne solution en C++ car cela utilise des variadic macros (https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html) ce qui n'est pas autorisé en ISO C++

    Voir un truc qui devait ressembler à ce que j'avais fait à l'époque :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdio.h>
     
    #define ENABLE_DEBUG    0
     
    #define DEBUG_BLOCK(x...)  {if(ENABLE_DEBUG) { x; }}
    #define DEBUG_MSG(x...)    {if(ENABLE_DEBUG) { printf(x); }}
     
    int main()
    {
        int i = 42;
     
        DEBUG_MSG("this is a debug message: %d\n", i);
     
        DEBUG_BLOCK
        (
        printf("this is a debug bloc\n");
        i = 84;
        )
     
        return i;
    }

  11. #11
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2011
    Messages
    476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : juin 2011
    Messages : 476
    Points : 1 996
    Points
    1 996

    Par défaut

    Les variadiques macros sont supportés à partir de c++11, mais sous la forme non-nommée en utilisant __VA_ARGS__ pour y faire référence.
    Dans les macros il est préférable de mettre les blocs dans un do { ... } while(0) pour rendre obligatoire le point virgule. Cela évite les surprises avec des if/else sans accolades.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    #define DEBUG_BLOCK(...)  do{if(ENABLE_DEBUG) { __VA_ARGS__; }}while(0)
    #define DEBUG_MSG(...)    do{if(ENABLE_DEBUG) { printf(__VA_ARGS__); }}while(0)
    Pour la première macro, le mieux est d'envoyer les paramètres à une fonction qui ne fait rien pour inhiber les avertissements du compilateur sur 1) les variables non initialisées 2) les expressions qui ne font rien. En plus, cette fonction pourrait être marquée comme étant un espèce de printf et faire systématiquement la vérification du format (option spécifique aux compilos par contre (attribut format)).

  12. #12
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur systèmes embarqués
    Inscrit en
    juin 2009
    Messages
    3 389
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur systèmes embarqués

    Informations forums :
    Inscription : juin 2009
    Messages : 3 389
    Points : 8 857
    Points
    8 857
    Billets dans le blog
    1

    Par défaut

    Ah merci pour ces précisions ! Cela compile aussi bien en C qu'en C++, sans warning, avec GCC. Je mettais souvent demandé l'intérêt des do/while(0) et effectivement le message d'erreur est parfait et le code utilisant les macros bien plus propre avec les ; obligatoires.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 18/01/2013, 10h37
  2. [Débutant] Plusieurs versions sur un même système ?
    Par castaka dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 15/02/2005, 10h21
  3. bout de code à dechifrer svp
    Par bball dans le forum C
    Réponses: 32
    Dernier message: 20/01/2005, 23h23
  4. Réponses: 2
    Dernier message: 05/12/2002, 16h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo