+ Répondre à la discussion
Page 1 sur 2 12 DernièreDernière
Affichage des résultats 1 à 20 sur 30
  1. #1
    Nouveau Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2010
    Messages : 32
    Points : 25
    Points
    25

    Par défaut Constantes, globales et portée, des mots qui font rêver.

    Bonjour,

    J'ai lu un peu partout qu'utiliser les macros pour définir ses constantes n'était pas la meilleure solution en C++. Il est de manière générale plutôt recommandé d'utiliser une variable const.
    e.g.
    Code :
    1
    2
    3
    4
    // Pas bien
    #define TEXT L"je suis théoriquement une mauvaise constante C++ :'("
    // Bien
    const wchar_t* text L"je suis théoriquement une bonne constante C++ :D";
    Oui mais voilà, ça me fait me poser tout un tas de question auxquelles ni mes recherches sur le web ou dans mes livres n'apportent de réponse. Je vous les liste donc point par point. Petite précision, j'utilise le C++11 dans la mesure ou mon compilateur (Visual c++ 2012) me le permet :

    1) J'ai lu que seules les constantes ayant un type intégral étaient valides en C++, et que c'est même l'une des limitations que le nouveau mot clé const expr du C++11 permettait de contourner. Seulement mon compilateur ne le supporte pas encore, et j'ai pourtant besoin de déclarer des constantes qui ne sont pas de type intégral comme des double ou des wchar_t. Comment suis-je censé m'y prendre dans ce cas là?

    2) Où dois-je déclarer mes constantes? Dans les fichiers source? Les headers? Est-ce que ça change quelque chose?

    3) Quelle est la différence concrète entre une constante et une globale? Sont-elles évaluées à la compilation?

    4) J'essaye d'utiliser le plus possible les unnamed namespaces en remplacement du mot clé static comme préconisé dans le nouveau standard du C++11. Mais je me demande si c'est vraiment nécessaire lorsque l'on est déjà dans un namespace comme dans l'exemple suivant :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    namespace rwl
    {
    namespace // Est-ce vraiment utile?
    {
    int useless_stuffs_in_this_code_count = 999999;
    }
     
    // Tout plein de fonctions dégueulasses qui utilisent ma variable.
     
    }
    Car il me semble bien qu'une variable déclarée dans un namespace a déjà sa portée limitée à ce dernier.

    5) Un cas concret maintenant. J'ai eu besoin de déclarer une constante accessible partout dans mon espace de nom "rwl". Je m'y suis prit de la manière suivante :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Ainsi, je n'ai qu'à inclure ce header pour bénéficier de ma constante là où j'en ai besoin. Est-ce propre? Existe t-il une meilleure solution?

    Voilà, en vous remerciant d'avance pour votre aide.

  2. #2
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Citation Envoyé par RT222 Voir le message

    1) J'ai lu que seules les constantes ayant un type intégral étaient valides en C++, et que c'est même l'une des limitations que le nouveau mot clé const expr du C++11 permettait de contourner. Seulement mon compilateur ne le supporte pas encore, et j'ai pourtant besoin de déclarer des constantes qui ne sont pas de type intégral comme des double ou des wchar_t. Comment suis-je censé m'y prendre dans ce cas là?
    Totalement faux.

    Je te fais un resume:

    1. N'importe quel type peut etre const en C++.
    2. En revanche, seul les integral peuvent etre utilise en paramettre template.
    3. const n'est pas synonyme d'immutable. Il faut bien comprendre la difference, meme si definir un objet static const rends l'objet theoriquement immutable (un const cast dessus t'emmene a "undefined behaviour" land)
    4. constexpr permet d'indiquer au compilateur qu'une fonction peut etre utilise pour generer une valeur a la compilation, ce qui sous entends que le resultat de la fonction peut etre utilise comme une constante immutable.
    5. constexpr peut aussi etre utilse pour les constructeurs, et donc les objets.
    6. Quand on declare un variables const, si c'est des integral, on peut les mettre dans des headers sans souci. Si ce sont des types utilisateurs (class/struct), les definir dans un header reviens a les redefinir dans tous les cpp qui incluent le header, et donc cela provoque une erreur de "multiple definition de symbole" au link.
    7. Pour eviter le probleme de 6, il suffit de faire la declaration de la constante dans le header, marke comme extern, puis la definition dans un seul unique cpp.
    8. Cela est valide pour les constantes definis dans les classes aussi (mais on n'a pas besoin de extern dans ce cas.)

    Je crois que j'ai fait le tour pour les bases. Dans ton cas, tu as juste a mettre la declaratoin dans un header, la definition dans un cpp.

    2) Où dois-je déclarer mes constantes? Dans les fichiers source? Les headers? Est-ce que ça change quelque chose?
    Oui, je crois que j'ai repondu dans ma precedente fonction.
    Garde en tete que:

    1. seul les fichiers cpp sont compiles
    2. les headers sont du contenu copie/colle dans les cpp qui l'incluent

    En partant de ca tu realises vite que si tu definirs quelque chose dans un header, il se retrouve duplique. C'est pas grave pour les entier, ca linkera pas pour les autres types.

    3) Quelle est la différence concrète entre une constante et une globale? Sont-elles évaluées à la compilation?
    Ce sont deux choses completement orthogonales.

    Seul ce qui est marque constexpr est evalue a la compilation SI les paramettres passes a la fonction (ou au constructeur) sont accessible a la compilation aussi.

    Theoriquement, certains objets marque static const (ou const mais en namespace anonyme - a confirmer) peuvent etre optimises par la compilateur, mais ce n'est pas garanti du tout.


    4) J'essaye d'utiliser le plus possible les unnamed namespaces en remplacement du mot clé static comme préconisé dans le nouveau standard du C++11. Mais je me demande si c'est vraiment nécessaire lorsque l'on est déjà dans un namespace comme dans l'exemple suivant :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    namespace rwl
    {
    namespace // Est-ce vraiment utile?
    {
    int useless_stuffs_in_this_code_count = 999999;
    }
     
    // Tout plein de fonctions dégueulasses qui utilisent ma variable.
     
    }
    Car il me semble bien qu'une variable déclarée dans un namespace a déjà sa portée limitée à ce dernier.
    Le namespace anonyme ne va pas changer le nom du namesapce au dessus. Ce qu'il fait c'est rendre son contenu accessible uniquement au cpp qui est compile. Cela reviens effectivement a ajouter static a tes variables, mais ca fait aussi isoler les fonctions qui sont interne a ton cpp.

    Note aussi que cela indique au compilateur qu'il ne doit surtout pas permettre de link avec d'autre cpp pour ces fonctions et donnees. En gros tu indique au compilateur de ne pas exposer les synboles dans le namespace anonyme de maniere a les isoler totalement et les reserver a l'usage d'un seul et unique cpp.

    Je crois pas que ce soit utile dans un header.

    5) Un cas concret maintenant. J'ai eu besoin de déclarer une constante accessible partout dans mon espace de nom "rwl". Je m'y suis prit de la manière suivante :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Ainsi, je n'ai qu'à inclure ce header pour bénéficier de ma constante là où j'en ai besoin. Est-ce propre? Existe t-il une meilleure solution?
    Mets la definition dans ton cpp, le namespace anonyme n'a aucun interet puisque tu DOIS exposer le symbole pour l'utiliser ailleur. Sinon tu vas provoquer des duplications de nom.....


    ...quoi que j'ai un doute, il se peut aussi que cela soit accepte par le compilateur mais generer un objet par cpp. JE vais tester voir.


    Sinon:

    header

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
          extern const wchar_t*	logfile_rwl; // declaration
     
    }
    cpp
    Code :
    1
    2
    3
    4
    5
    6
    7
     
    #include "config.h"
    namespace rwl
    {
           const wchar_t*	logfile_rwl = L"Logfiles/rwl.log"; // definition
     
    }
    (moi je l'aurai mis dans un sous namespace reserve aux constantes mais bon ca depends de ce que tu fais - j'aurais aussi mis les constantes en GRANDES_LETTRES_AVEC_UNDERSCORE mais c'est a toi de voir).


    EDIT> J'ai oublie les 'extern'!

  3. #3
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Apparement GCC 4.7 et VS2012 acceptent les multiple definitions si c'est dans un namespace anonymes. Pas certain que ce soit standard, mais je vais demander.

  4. #4
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    J'ai demande la: https://groups.google.com/a/isocpp.o...on/IQOJOP_vM5M


    Donc en gros

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Est tout a fait valide et fera ce que tu penses.

    La subtilite c'est que l'objet (ou plutot un clone) va etre definis une fois par cpp qui inclus le fichier.

    A priori c'est un moyen inferieur de declarer des constantes (pour les non-constantes je vois bien une utilite...) compare au couble header+cpp sauf qu'on a un seul fichier a gerer pour les definitions.


    EDIT:

    On Mon, Feb 4, 2013 at 11:23 AM, Johannes Schaub
    <schaub.johannes@googlemail.com> wrote:

    Unnamed namespaces should only be used in a header for data (but i still prefer just declaring them "const"). And only when the data in it is const.

    Mutable data wont carry modifications outside a TU which is bad if the declaration was in a header.

    And for functions, making them static or in an unnamed namespace easily will bloat the executable. Either make it inline, so that you have only one function. Or define it in the cpp file (which we want to avoid for the purpose of discussion).

    And for classes, the reasoning is the same as fir nonconst data. If they are ina header, they ought to have extern linkage and be the same for every inclusion. Otherwise, put it not in a header.

    Another good argument for not splitting const data up into a cpp file is avoiding the static initialization order fiasco that happens when you use the data value in the initialization of another nonlocal nonmember object.
    Donc en gros, ta definition est bonne et a meme un avantage concernant l'ordre d'initialization. Cela dis, la taille de l'exe va dependre des optimizations au link...

  5. #5
    Membre Expert
    Inscrit en
    novembre 2004
    Messages
    2 559
    Détails du profil
    Informations forums :
    Inscription : novembre 2004
    Messages : 2 559
    Points : 2 172
    Points
    2 172

    Par défaut

    Je rappelle que la traduction d'"integral", c'est "entier(e)"...
    VDS "The C++ Standard Library" (Josuttis) -> 20 €

  6. #6
    Modérateur
    Avatar de koala01
    Inscrit en
    octobre 2004
    Messages
    9 783
    Détails du profil
    Informations personnelles :
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 783
    Points : 17 346
    Points
    17 346

    Par défaut

    Salut,

    Et, ce qui n'a pas été écrit jusqu'à présent... :

    Il faut rappeler que les variables (constantes ou non) globales, c'est mal.

    La raison est bien simple: une globale est, comme son nom l'indique, disponible depuis "n'importe où".

    Déjà, il est très rare d'avoir réellement besoin de quelque chose depuis "n'importe où": au pire, on a généralement besoin de quelque chose à "plusieurs endroits clairement identifiés", mais ce n'est pas pareil

    Ensuite, si une variable globale n'est pas constante, le fait qu'elle soit accessible depuis n'importe où fait que l'on perd tout contrôle sur les modifications que peut subir cette variable, ce qui rend le débuggage franchement difficile si, pour une raison ou une autre, la valeur ne correspond pas à ce que l'on croit qu'elle devrait être
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre Expert
    Inscrit en
    novembre 2004
    Messages
    2 559
    Détails du profil
    Informations forums :
    Inscription : novembre 2004
    Messages : 2 559
    Points : 2 172
    Points
    2 172

    Par défaut

    Je ne serais pas aussi général.

    Le cas typique, c'est de définir la valeur de pi, par exemple.
    VDS "The C++ Standard Library" (Josuttis) -> 20 €

  8. #8
    Expert Confirmé Sénior

    Homme Profil pro
    Développeur informatique
    Inscrit en
    septembre 2007
    Messages
    1 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : septembre 2007
    Messages : 1 894
    Points : 4 451
    Points
    4 451

    Par défaut

    Citation Envoyé par Klaim Voir le message
    Apparement GCC 4.7 et VS2012 acceptent les multiple definitions si c'est dans un namespace anonymes. Pas certain que ce soit standard, mais je vais demander.
    Au niveau des compilateurs, un namespace anonyme défini dans une unité de compilation crée un pseudo-namespace nommé de manière unique (ce nom ne t'es pas accessible). Du coup, deux namespace anonyme dans deux unités de compilation sont nécessairement différents, et le même symbole peut y être défini plusieurs fois (l'ODR est quand même respecté).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  9. #9
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Citation Envoyé par oodini Voir le message
    Je ne serais pas aussi général.

    Le cas typique, c'est de définir la valeur de pi, par exemple.
    De meme, je ne vois rien de mal avec les constantes expose globalement si elles definissent bien des constantes d'un domaine. Tant qu'elles sont sous namespace.

    Contrairement aux objets variable, on ne partage pas l'etat d'une constante.

  10. #10
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Citation Envoyé par Emmanuel Deloget Voir le message
    Au niveau des compilateurs, un namespace anonyme défini dans une unité de compilation crée un pseudo-namespace nommé de manière unique (ce nom ne t'es pas accessible). Du coup, deux namespace anonyme dans deux unités de compilation sont nécessairement différents, et le même symbole peut y être défini plusieurs fois (l'ODR est quand même respecté).
    C'est ce qu'on m'a confirme dans les messages suivants.

  11. #11
    Nouveau Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2010
    Messages : 32
    Points : 25
    Points
    25

    Par défaut

    Tout me paraît tellement plus clair maintenant! Un grand merci à vous pour vos réponses, et particulièrement à Klaim. Vous ne pouvez pas savoir à quel point ça m'aide.

    Concernant la constante que j'utilise, je comprends l'avertissement de Koala01 et j'essaye d'éviter au maximum les constantes globales (et je m'interdis les globales tout court). D'ailleurs celle que j'ai montrée ici est la seule que j'utilise dans mon programme. J'en ai eu besoin car elle me sert à configurer le fichier dans lequel les messages d'erreur loggés depuis mon espace de nom rwl seront écrits. Et comme je logge un petit peu partout...

    j'aurais aussi mis les constantes en GRANDES_LETTRES_AVEC_UNDERSCORE mais c'est a toi de voir
    Dans ma norme ce sont uniquement les directives préprocesseurs qui sont en majuscule, car j'avais lu un article intéressant qui le recommandait et avait des arguments fort pertinents pour le justifier.
    [EDIT] : Je l'ai retrouvé : http://blog.ploki.info/post/25160737...-en-majuscules


    J'aurais juste deux questions supplémentaires :

    1) Comme, pour des raisons imposées par l'architecture de mon programme, je suis obligé de définir ma constante uniquement dans le header, et que je n'aime pas trop l'idée qu'elle soit dupliquée dans chaque cpp où elle est incluse (ça me paraît sale), ne serait-il pas mieux dans ce cas précis d'utiliser une macro?

    2) J'ai des fonctions que je souhaite rendre inaccessible en dehors de la TU où elles sont définies. J'ai tenté de procéder ainsi, comme je l'aurais fait avec une variable :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    ** source.cpp
    */
    namespace rwl
    {
    namespace
    {
    void fonction();
    }
    void fonction()
    {
    }
    }
    Sauf que ça ne compile pas : "unresolved external symbol". En revanche, si j'enlève le namespace anonyme et que j'utilise le mot clé static, le programme compile parfaitement, et mes fonctions sont effectivement inaccessibles en dehors de leur TU. Ce comportement est-il normal?

  12. #12
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Citation Envoyé par RT222 Voir le message
    1) Comme, pour des raisons imposées par l'architecture de mon programme, je suis obligé de définir ma constante uniquement dans le header, et que je n'aime pas trop l'idée qu'elle soit dupliquée dans chaque cpp où elle est incluse (ça me paraît sale), ne serait-il pas mieux dans ce cas précis d'utiliser une macro?

    Dans le cadre d'une constante non-entier? La macro ne ferait que copier coller le code, ce qui est une pire situation que si tu mets la constante dans un namespace anonyme. Au moins dans ce dernier cas tu sais que la constante sera instanciee une seule unique fois. Si tu utilises un macro, tu perds cette garantie, et meme tu es presque garanti de l'inverse.

    Donc je dirais non.

    Ou alors ca depends de la gueule de la macro.




    2) J'ai des fonctions que je souhaite rendre inaccessible en dehors de la TU où elles sont définies. J'ai tenté de procéder ainsi, comme je l'aurais fait avec une variable :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    ** source.cpp
    */
    namespace rwl
    {
    namespace
    {
    void fonction();
    }
    void fonction()
    {
    }
    }
    Sauf que ça ne compile pas : "unresolved external symbol". En revanche, si j'enlève le namespace anonyme et que j'utilise le mot clé static, le programme compile parfaitement, et mes fonctions sont effectivement inaccessibles en dehors de leur TU. Ce comportement est-il normal?


    La tu declares/definis deux fonctions differentes, d'ou l'erreur; l'une avec internal linkage, l'autre avec external linkage.
    Si ta fonction n'est qu'a destination du TU en cours, tu dois tout mettre dans le namespace anonyme, que ce soit declaration ou definition, ils doivent avoir le meme linkage (de la meme maniere que tu vas pas declarer un objet statique puis pas statique).

  13. #13
    Nouveau Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2010
    Messages : 32
    Points : 25
    Points
    25

    Par défaut

    La tu declares/definis deux fonctions differentes, d'ou l'erreur; l'une avec internal linkage, l'autre avec external linkage.
    Si ta fonction n'est qu'a destination du TU en cours, tu dois tout mettre dans le namespace anonyme, que ce soit declaration ou definition, ils doivent avoir le meme linkage (de la meme maniere que tu vas pas declarer un objet statique puis pas statique).
    Effectivement, je n'y avais pas pensé. Je viens de corriger ça dans mon code et ça marche parfaitement.

    Encore merci pour ton aide.

  14. #14
    Expert Confirmé Sénior

    Avatar de germinolegrand
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    octobre 2010
    Messages
    733
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : octobre 2010
    Messages : 733
    Points : 4 119
    Points
    4 119

    Par défaut

    Citation Envoyé par koala01 Voir le message
    Salut,

    Et, ce qui n'a pas été écrit jusqu'à présent... :

    Il faut rappeler que les variables (constantes ou non) globales, c'est mal.

    La raison est bien simple: une globale est, comme son nom l'indique, disponible depuis "n'importe où".

    Déjà, il est très rare d'avoir réellement besoin de quelque chose depuis "n'importe où": au pire, on a généralement besoin de quelque chose à "plusieurs endroits clairement identifiés", mais ce n'est pas pareil

    Ensuite, si une variable globale n'est pas constante, le fait qu'elle soit accessible depuis n'importe où fait que l'on perd tout contrôle sur les modifications que peut subir cette variable, ce qui rend le débuggage franchement difficile si, pour une raison ou une autre, la valeur ne correspond pas à ce que l'on croit qu'elle devrait être
    Autant pour les variables globales j'approuve totalement, autant pour les constantes globales (parler de variable constante me semble paradoxal ) je ne vois pas en quoi c'est mal si c'est à usage général (exemple de pi donné plus haut par exemple). Ce serait comme dire que les fonctions libres manipulant des paramètres constants c'est mal... ça n'a pas beaucoup de sens .

    Inclues-tu dans ta remarque les objets constants définis dans des namespace ou bien ta remarque se limite-t-elle à l'espace global ?
    Choisis un travail que tu aimes et tu n'auras pas à travailler un seul jour de ta vie.

    FYS : une bibliothèque C++ dans le domaine public (discussion : [fr])

    Dernier article : Le C++14 est arrivé !

  15. #15
    Modérateur
    Avatar de koala01
    Inscrit en
    octobre 2004
    Messages
    9 783
    Détails du profil
    Informations personnelles :
    Âge : 42

    Informations forums :
    Inscription : octobre 2004
    Messages : 9 783
    Points : 17 346
    Points
    17 346

    Par défaut

    Citation Envoyé par RT222 Voir le message
    Concernant la constante que j'utilise, je comprends l'avertissement de Koala01 et j'essaye d'éviter au maximum les constantes globales (et je m'interdis les globales tout court). D'ailleurs celle que j'ai montrée ici est la seule que j'utilise dans mon programme. J'en ai eu besoin car elle me sert à configurer le fichier dans lequel les messages d'erreur loggés depuis mon espace de nom rwl seront écrits. Et comme je logge un petit peu partout...
    Mais peut etre pourrais tu fortement restreindre la portée de ta variable globale

    Je m'explique:
    1. D'abord, il faut savoir que "global" peut vouloir dire beaucoup de choses comme:
    2. global au sein d'une fonction ( dans le premier niveau d'accolades, par exemple)
    3. global au sein d'un fichier d'implémentation (et donc uniquement accessible dans celui-ci
    4. global au sein d'un module (mais inaccessible en dehors de celui-ci), sans doute parce que déclarée dans un fichier d'en-tête qui n'est pas accessible lorsque le module est utilisé (mais bien lorsque le module est compilé)
    5. global au sein d'une application, éventuellement au sein d'une application qui utilise un module dans lequel la variable est déclarée

    Le principe de base est que plus la globalité s'étend, plus il faut que les raisons de cette extension soient importantes et réelles

    Pour décider de définir une globale au niveau de l'application, il faut par exemple que la valeur en question soit issue d'un domaine tout à fait extérieur à l'informatique (maths, physique, chimie, ou que sais-je).

    Pi, le nombre d'avogadro, le volume d'un gaz parfait, la valeur de la gravité sur mars ou que sais-je encore sont autant de valeurs qui méritent amplement d'être globales au niveau de l'application car les domaines qui les utilisent sont susceptibles d'en avoir besoin "à peu près n'importe quand".

    Mais il ne faut voir là que l'exception qui confirme la règle, car il est tout à fait possible, par exemple, d'éviter le recours à des globales au sein d'un module donné (à vrai dire, je n'ai pas encore trouvé de raison cohérente et suffisante pour décider de créer une globale au niveau du module ).

    Si l'on regarde, par exemple, ce que fait C avec sa structure FILE, on se rend compte qu'il est tout à fait possible de fournir une série de fonctions qui agissent sur "quelque chose" dont on ignore tout.

    Dés lors, qu'est ce qui t'empêcherait d'exposer (sur base de considérations qui n'appartiennent qu'à toi) qu'une série de fonctions qui manipuleraient tes données sans exposer ces données

    Par exemple, j'ai récemment écrit un fichier d'en-tête pour du multi logging qui ressemble à peu près à
    Code :
    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
    /* la classe de base pour les différents loggers */
    class LoggerBase;
    /* permet d'enregistrer un nouveau logger auprès du système qui les gère 
     */
    void registerLogger(LoggerBase const & toRegister);
    /* permet d'activer un logger identifié par son nom */
    void enableLogger(std::string const &  name);
    /* permet de désactiver un logger identifié par son nom */
    void disableLogger(std::string const & name);
    /* permet (c'est parfois utile) de savoir si un logger donné est activé */
     bool loggerEnabled(std::string const & name);
    /* permet d'activer un niveau de logging donné pour un logger identifié
     * par son nom
     */
    void enableLevel(std::string const & name, LogLevel lvl);
    /* permet de désactiver un niveau de loggin donné pour un logger
     * identifié par son nom
     */
    void disableLevel(std::string const & name, LogLevel lvl);
    /* permet (c'est parfois utile) de savoir si un niveau delogging donné est 
     * pour un logger donné
     */
    bool levelEnabled(std::string const & name, LogLevel lvl);
    /* permet de logger un message pour un niveau de logging donné
     * (tous loggers pour lequel le niveau est activé confondus)
     */
    void log(LogLevel lvl, std::string const & message,
                        std::string const & file, size_t line);
     
    /* permet de logger un message pour un niveau de logging donné
     * pour un logger identifié par son nom
     */
    void log(std::string const & name, LogLevel lvl,
                        std::string const & message, std::string const & file,
                        size_t line);
    /* permet d'activer la numérotation des lignes pour un logger donné
     */
    void enableCount(std::string const & name);
     
    /* permet de désactiver la numérotation des lignes pour un logger donné
     */
    void disableCount(std::string const & name);
     
    /* permet de savoir si la numérotation des lignes pour un logger donné
     * est activée
     */
    bool countEnabled(std::string const & name );
    Et devine quoi

    Toutes ces fonctions manipulent un objet "singleton" (mon dieu, quelle horreur !!!) dont personne n'entendra jamais parler s'il ne va pas voir directement à l'intérieur du fichier d'implémentation.

    Pourquoi, me demanderas-tu

    Simplement parce que j'ai un tout petit peu changé ma manière de faire (et c'est tout à fait légal ): tout ce qui a trait à ce singletonest déclaré et défini directement dans le fichier d'implémentation

    Mais le résultat est génial: je peux utiliser ces fonctions strictement où je veux, en ayant la certitude que personne n'ira jamais toucher à l'instance de mon singleton.

    Je dois cependant reconnaitre une légère contrepartie: les fonctions dont j'ai donné les déclarations plus haut ne sont, fatalement, pas inlinées (par contre, toutes celles de mon singleton le sont ), mais cela peut provoquer (il faudrait que je compare ) une éventuelle perte de performances
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  16. #16
    Nouveau Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2010
    Messages : 32
    Points : 25
    Points
    25

    Par défaut

    J'ai déjà pensé à un Logger en Singleton, mais j'ai finalement opté pour une implémentation plus primaire qui convient parfaitement à mes besoins. C'est avec appréhension que je vous la présente car j'ai peur que ce code me vaille quelques reproches :

    Code :
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    #include <RWL/Cpp_Tools/logger.hpp>
    #include <Boost/iostreams/stream.hpp>
    #include <Boost/iostreams/tee.hpp>
    #include <fstream>
    #include <map>
    #include <locale>
    #include <codecvt>
     
    namespace rwl
    {
     
    namespace logger
    {
     
    namespace
    {
     
    /*
    ** Permet de sauvegarder et de récupérer les fichiers précédemment utilisés, afin
    ** d'éviter de les ouvrir/fermer à chaque appel à mon logger. Oui je sais, la map
    ** est probablement un peu abusée pour le faible nombre de fichiers que ça
    ** représente (une dizaine en moyenne), mais au moins c'est rapide. : P
    */
    bool set_wfile( const std::wstring& file_path = L"" ); // Et voilà pourquoi j'avais besoin de masquer une fonction avec un namespace anonyme.
     
    bool set_wfile( const std::wstring& file_path /*= L"" */ )
    {
    	static std::map< std::wstring, std::wofstream >		wfiles;
    	std::map< std::wstring, std::wofstream >::iterator	file_it = wfiles.end();
     
    	file_it = wfiles.find( file_path );
    	if ( file_it != wfiles.end() )
    	{
    		std::wclog.rdbuf( file_it->second.rdbuf() );
    		return ( true );
    	}
    	else
    	{
    		wfiles.emplace( file_path, std::wofstream( file_path, std::ios::trunc | std::ios::out ) );
     
    		std::wofstream&	logfile = wfiles.at( file_path );
     
    		if ( logfile.is_open() && logfile.good() )
    		{
    			logfile.imbue( std::locale( logfile.getloc(), new ( std::codecvt_utf8_utf16< wchar_t > ) ) );
    			std::wclog.rdbuf( logfile.rdbuf() );
    			return ( true );
    		}
    		std::wclog.rdbuf( NULL );
    		wfiles.erase( file_path );
    	}
    	return ( false );
    }
     
    }
     
    typedef boost::iostreams::tee_device< std::wostream, std::wostream > Wtee;
    typedef boost::iostreams::stream< Wtee > Wstream;
     
    /*
    ** Le logger. Le fichier est facultatif. Si je n'en spécifie pas, ou s'il est invalide,
    ** ça ne logge que dans le ostream envoyé. Si je lui demande de logger dans wclog,
    ** alors il n'écrira que dans le fichier de log. De cette manière, je n'ai plus qu'à
    ** rediriger les sorties standards dans ma console de debug et tout marche parfaitement.
    */
    std::wostream& log( std::wostream& stream, const std::wstring& file_path /*= L"" */ )
    {
    	bool	is_file_ok = set_wfile( file_path );
     
    	if ( is_file_ok && stream != std::wclog )
    	{
    		static Wtee		tee( stream, std::wclog );
    		static Wstream	tstream( tee );
     
    		return ( tstream );
    	}
     
    	return ( stream );
    }
     
    }
     
    }
    Y'a aussi une version surchargée non wide, mais je ne l'ai pas mise ici parce que c'est sensiblement la même. Donc pour logger, par exemple une erreur, j'ai simplement à écrire :

    Code :
    rwl::logger::log( std::wcerr, logfile_rwl ) << L"Error: Ca marche pas! :'(" << std::endl; // Je vous rassure, mes messages d'erreur sont un poil plus pertinents dans mon code ^^.
    La contrepartie de cette façon de faire, c'est qu'il me faut spécifier le fichier dans le lequel je souhaite logger à chaque fois. J'aurais pu faire une autre fonction qui permette de définir un fichier de log commun à tous les appels suivants à mon logger, seulement il m'arrive d'avoir à changer très fréquemment de fichier (pour isoler telle ou telle donnée) et le fait de le spécifier à chaque fois me permet de bien savoir où j'écris.

    Et donc voilà pourquoi j'ai besoin d'une globale. A l'intérieur de mon espace de nom rwl, je logge essentiellement dans un même fichier que j'aurais eu à redéclarer de nombreuses fois partout sans ma constante.

    Après je ne dis pas que c'est une bonne façon de faire, et si vous me dites que c'est même carrément crado je m'appliquerais à réécrire tout ça de manière plus propre.

  17. #17
    Expert Confirmé Sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    septembre 2005
    Messages
    24 347
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2005
    Messages : 24 347
    Points : 34 752
    Points
    34 752

    Par défaut

    Franchement, plutôt que "globale dans un namespace", je tends à préférer "statique dans une classe". Avec ça, la classe peut gérer l’instanciation de l'objet en question.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  18. #18
    Nouveau Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2010
    Messages
    32
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2010
    Messages : 32
    Points : 25
    Points
    25

    Par défaut

    Bon, après avoir avancé dans mon projet, et après avoir eu l'occasion d'utiliser mon "logger" (qui n'en est pas vraiment un...) en situation réelle, je me suis rendu compte des limites de ce dernier. En fait je me suis surtout rendu compte qu'il ne me servait à rien du tout. Du coup j'ai relu la réponse de Koala, fait plus attention à la structure de son logger, et après avoir traficoté à droite et à gauche sur le net pour piocher des idées dans des librairies de logger déjà existantes (notamment celle de Boost), me revoilà avec une toute nouvelle implémentation :

    Code :
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    /*
    ** logger.hpp
    */
    #pragma once
     
    #include <RWL/Cpp_Tools/class_templates.hpp>
    #include <RWL/Cpp_Tools/debug.hpp>
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <fstream>
    #include <map>
    #include <memory>
     
    #define LOG_LOCATION	L" [" << __WFILE__ << L", line " << __LINE__ << L"]"
     
    namespace rwl
    {
     
    class Logger :
    	private Non_copyable
    {
    public :
    	enum class Level
    	{
    		trace,
    		debug,
    		info,
    		warning,
    		error,
    		exception,
    		fatal
    	};
     
    							Logger( const std::wstring& name = L"" );
    							~Logger( void );
    	std::wostringstream&	log( Level level );
     
    private :
    	std::wstring		m_name;
    	std::wostringstream	m_logbuffer;
    	Level				m_level;
     
    public :
    	static void enable( bool enable, const std::wstring& name = L"" );
    	static void set_level( Level level, const std::wstring& name = L"" );
    	static void	set_logfile( const std::wstring& file_path, const std::wstring& name = L"" );
     
    private:
    	class Logger_data
    	{
    	public :
    						Logger_data( void );
    						Logger_data( const Logger_data& logger_data );
    						~Logger_data( void );
    		Logger_data&	operator=( const Logger_data& logger_data );
     
    		bool								enable;
    		Level								level;
    		std::shared_ptr< std::wofstream >	logfile;
    	};
    	typedef std::map< const std::wstring, Logger_data > Logger_data_map;
     
    	static Logger_data_map	m_loggers_data;
    };
     
    }
     
    /*
    ** logger.cpp
    */
    #include <RWL/Cpp_Tools/logger.hpp>
    #include <locale>
    #include <codecvt>
     
    namespace rwl
    {
     
    Logger::Logger_data::Logger_data( void ) :
    	enable( true ),
    	level( Logger::Level::trace ),
    	logfile( std::make_shared< std::wofstream >() )
    {
     
    }
     
    Logger::Logger_data::Logger_data( const Logger::Logger_data& logger_data ) : 
    	enable( logger_data.enable ),
    	level( logger_data.level ),
    	logfile( logger_data.logfile )
    {
     
    }
     
    Logger::Logger_data::~Logger_data( void )
    {
    	logfile->close();
    }
     
    Logger::Logger_data& Logger::Logger_data::operator=( const Logger_data& logger_data )
    {
    	enable = logger_data.enable;
    	level =logger_data.level;
    	logfile = logger_data.logfile;
    	return ( *this );
    }
     
    Logger::Logger_data_map Logger::m_loggers_data;
     
    Logger::Logger( const std::wstring& name /*= L"" */ ) :
    	m_name( name )
    {
     
    }
     
    Logger::~Logger( void )
    {
    	Logger_data&	logger_data = m_loggers_data[ m_name ];
    	std::wstring	header;
     
    	if ( logger_data.enable && m_level >= logger_data.level )
    	{
    		switch ( m_level )
    		{
    		case ( Logger::Level::trace ) :
    			header = L"[Trace]";
    			break;
    		case ( Logger::Level::debug ) :
    			header = L"[Debug]";
    			break;
    		case ( Logger::Level::info ) :
    			header = L"[Info]";
    			break;
    		case ( Logger::Level::warning ) :
    			header = L"[Warning]";
    			break;
    		case ( Logger::Level::error ) :
    			header = L"[Error]";
    			break;
    		case ( Logger::Level::exception ) :
    			header = L"[Exception]";
    			break;
    		case ( Logger::Level::fatal ) :
    			header = L"[Fatal]";
    			break;
    		}
     
    		if ( m_name != L"" )
    		{
    			header += L"[";
    			header += m_name;
    			header += L"]";
    		}
     
    		header+= L": ";
     
    		if ( m_level <= Logger::Level::info )
    			std::wcout << header << m_logbuffer.str() << std::flush;
    		else if ( m_level >= Logger::Level::warning )
    			std::wcerr << header << m_logbuffer.str() << std::flush;
     
    		if ( logger_data.logfile )
    			if ( logger_data.logfile->is_open() && logger_data.logfile->good() )
    				*logger_data.logfile << header << m_logbuffer.str() << std::flush;
    	}
    }
     
    std::wostringstream& Logger::log( Level level )
    {
    	m_level = level;
    	return ( m_logbuffer );
    }
     
    void Logger::enable( bool enable, const std::wstring& name /*= L"" */ )
    {
    	m_loggers_data[ name ].enable = enable;
    }
     
    void Logger::set_level( Level level, const std::wstring& name /*= L"" */ )
    {
    	m_loggers_data[ name ].level = level;
    }
     
    void Logger::set_logfile( const std::wstring& file_path, const std::wstring& name /*= L"" */ )
    {
    	if ( m_loggers_data[ name ].logfile )
    	{
    		std::wofstream& logfile = *m_loggers_data[ name ].logfile;
     
    		logfile.open( file_path, std::ios::trunc | std::ios::out );
    		if ( logfile.is_open() && logfile.good() )
    			logfile.imbue( std::locale( logfile.getloc(), new ( std::codecvt_utf8_utf16< wchar_t > ) ) );
    		else
    			logfile.close();
    	}
    }
     
    }
    Et ça s'utilise comme ça :

    Code :
    1
    2
    rwl::Logger::set_logfile( L"rwl.log", L"RWL" );
    rwl::Logger( L"RWL" ).log( rwl::Logger::Level::debug ) << L"Coucou, tu veux voir mon logger?" << std::endl;
    Du coup plus de constante globale! Et en bonus j'ai enfin un vrai logger (dont je suis fier d'ailleurs, même si je ne devrais probablement pas ).

    Voilà, je n'ai aucune question supplémentaire, c'était juste pour vous remercier à nouveau pour vos précieux conseils et vous montrer qu'ils ne sont pas ignorés.

  19. #19
    Expert Confirmé Sénior

    Homme Profil pro
    Développeur informatique
    Inscrit en
    septembre 2007
    Messages
    1 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : septembre 2007
    Messages : 1 894
    Points : 4 451
    Points
    4 451

    Par défaut

    Je préviens tout de suite : je suis critique, mais je ne pense pas à mal. Il y a plusieurs erreurs dans le code, et je vais les présenter de manière neutre (pas méchante, ni gentillette ; ton neutre, tout ça).

    Citation Envoyé par RT222 Voir le message
    Et ça s'utilise comme ça :

    Code :
    1
    2
    rwl::Logger::set_logfile( L"rwl.log", L"RWL" );
    rwl::Logger( L"RWL" ).log( rwl::Logger::Level::debug ) << L"Coucou, tu veux voir mon logger?" << std::endl;
    Du coup plus de constante globale! Et en bonus j'ai enfin un vrai logger (dont je suis fier d'ailleurs, même si je ne devrais probablement pas ).
    Je confirme

    Dans un premier temps : le fait de logger en RAM est une très, très mauvaise idée. Tu veux du log parce que tu estimes que tu va avoir des problèmes. Là, si ton programme plante, tu perds tout ton log. Un système de log est censé être immédiat - dès qu'il y a quelque chose à logger, l'information se retrouve stockée dans un système de stockage persistant (ça peut être de la RAM, dès lors qu'elle est partagée avec l'extérieur et que la mort du programme loggué ne tue pas la zone de mémoire partagée ; mais la plupart du temps, c'est un fichier ou un pseudo-fichier (socket, char device (sous Linux)...).

    Ensuite, ta fonction de log + l'accès à celle-ci via un singleton rien par rapport à une simple fonction :

    Code :
    rwl::log(const std::wstring& logid, const rwl::loglevel&)
    Tu n'as pas de globale, mais tu as une statique de classe (qui est une globale aussi ; elle est stockée au même endroit dans la RAM, et est initialisée au même moment). Je sais qu'on t'a dit que c'est mieux, et dans un sens, c'est vrai. Mais dans ce cas précis, ça ne change strictement rien.

    Ensuite, même en considérant que ton logger protège l'accès à la map sous-jacente, que se passe-t-il lorsque deux thread essaient d'écrire dans le même fichier de log ?

    La réponse exacte est multiple. Les stream C++ ne sont pas threadsafe. Dans le meilleur des cas, tu vas avoir l'illusion que tout fonctionne. Un cas intermédiaire : tes lignes de log sont mélangées, certaines parties du log ne sont pas présentes - et le log n'est au final pas fiable. Pire cas : l'explosion du soleil (ok, c'est rare ; mais ça peut peut-être arriver...).

    Un dernier problème : le log étant identifié par une valeur qui n'est connue qu'au runtime, tu as une possibilité d'erreur supplémentaire : si tu t'es trompé dans la valeur l'identifiant, le problème ne peut être décelé qu'au runtime, pas à la compilation.

    Bref, au final, la solution n'apporte pas grand chose par rapport à (j'élimine le coté unicode pour simplifier l'écriture du code):

    Code :
    1
    2
    3
     
    std::ofstream rwl_logger("rwl.log"); // globale, comme std::cout :)
    rwl_logger << log_level << "les infos que je veux logger" << std::endl;
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  20. #20
    Expert Confirmé
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 334
    Points
    3 334

    Par défaut

    Ya qu'a voir Boost.Log pour comprendre que le logging est pas un sujet facile. Si tu avais vraiement ete inspire par Boost.Log, tu aurais plutot fait un objet LogRecord qui contient les infos a logge, et une fonction comme suggere par Emmanuel, qui prends juste cet objet et se debrouille avec. Ensuite dans l'implementation, tu peux faire differentes choses dont ecrire sur un fichier ET FLUSHER IMMEDIATEMENT. Tout cela thread-safe evidemment.

Liens sociaux

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
  •