IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Restriction sur une fonction template


Sujet :

Langage C++

  1. #1
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut Restriction sur une fonction template
    Bonjour à tous !
    J'ai une question dans la lignée du sujet Restriction sur une classe template (dont j'étudie attentivement les réponses )

    Dans les briques de bases de mon appli, il y a les fichiers suivant :

    Error .h qui définit tous les codes d'erreurs utilisés dans le projet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    enum Error
    {
    // code d'erreur...
    };
    ErrorIO.h qui permet de convertir un code d'erreur entier en chaine lisible par un humain. Ce fichier est en C et il est d'ailleurs inclus dans un sous-projet en C.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    #include "Error.h"
    char* to_string(Error err);
    et string_utility.h qui fournit une fonction de conversion générique vers string.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    #include "Error.h"
    template<typename T>
    std::string to_string(const T&)
    {
        std::ostringstream oss;
        oss << Value;
        return oss.str();
    }
    Il n'y a aucune relation entre ErrorIO.h et string_utility.h à part l'include commun de Error.h. Notez aussi que les deux fonctions to_string ont des types de retour différents...

    Je cherche à interdire dans mon code de plus haut niveau l'appel de la fonction to_string générique de string_utility.h quand le paramètre est un code d'erreur et forcer le client à utiliser la to_string() de ErrorIO.h

    Dans l'idéal il suffirait de bien vérifier que string_utility.h n'est pas inclus et que ErrorIO.h le soit, mais vu que string_utility.h se retrouve inclus un peu partout dans le projet c'est presque impossible d'être sur.

    En C++0x, il me semble qu'on pourrait résoudre le problème en supprimant explicitement la spécialisation pour une Error avec =delete
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // Please don't use the generic to_string() function for error codes. 
    // Include instead "ErrorIO.h" wich provide the correct to_string function for them.
    template <>
    std::string to_string<Error>(const Error&) = delete;
    Si la personne qui cherche à faire un to_string() avec un Error n'a inclus que string_utility.h alors la compilation planterait sur la ligne du =delete, elle verrait le commentaire, inclurait ErrorIO.h et voila !

    Mais comment fait-on en pur C++03 ? (et sans accès à boost)
    Merci !

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Pourquoi ne pas, simplement, faire confiance au préprocesseur avec une directive consacrée
    Dans l'un ou l'autre des fichiers d'en-tête, tu pourrait rajouter (en se basant sur le fait que les garde contre l'inclusions multiples soient respectivement #define ERROR_H et STRING_UTILITY_H)

    si c'est dans error.h cela prendrait une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #if defined(STRING_UTILIY_H)
    #error this file may not be included with string_utility.h, please correct your code
    #endif
    et, si tu le place dans string_utility.h, cela prendrait une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #if defined(ERROR_H)
    #error this file may not be included with error.h, please correct your code
    #endif
    (en dehors des gardes anti inclusions multiples
    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

  3. #3
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Dans la fonction générique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template< typename T>
    disable_if<is_same<T,Error>, std::string> to_string(const T&)
    {
        std::ostringstream oss;
        oss << Value;
        return oss.str();
    }
    is_same étant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <typename T, typename U>
    struct is_same
    {
      enum { value = false; }
    };
     
    template <typename T>
    struct is_same<T,T>
    {
      enum { value = true; }
    };
    Quelque chose de ce genre devrait marcher.

    PS : disable_if est laissée à titre d'exo

  4. #4
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Merci Alp !

    Bon, pour disable_if j'ai simplement copié boost comme un misérable tacheron...
    On obtient ça :

    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
    57
    58
    59
    60
     
    #include <string>
    #include <sstream>
    #include <iostream>
     
    enum Error
    {
       ok,
       ko
    };
     
    template <typename T, typename U>
    struct is_same
    {
      enum { value = false };
    };
     
    template <typename T>
    struct is_same<T , T>
    {
      enum { value = true };
    };
     
    template <bool B, class T = void>
    struct disable_if_c
    {
       typedef T type;
    };
     
    template <class T>
    struct disable_if_c<true, T>
    {
    };
     
    template <class Cond, class T = void> 
    struct disable_if : public disable_if_c<Cond::value, T> 
    {
    };
     
     
    template< typename T>
    typename disable_if<is_same<T, Error>, std::string>::type
    to_string(const T& value)
    {
        std::ostringstream oss;
        oss << value;
        return oss.str();
    }
     
     
    int main()
    {
        int i = 5;
        std::string s1 = to_string(i);
     
        std::cout << s1 << std::endl;
     
        Error err = ok;
        std::string s2 = to_string(err);
    }
    Qui marche

    Par contre je suis un peu décu par le message d'erreur... J'espérais qu'il redirigerait l'utilisateur vers la fonction to_string, fonction auquelle j'aurais pu alors rajouter un commentaire expliquant la situation. Mais, finalement, sous vs2005, on est bloqué sur la ligne s2 = to_string(err) et forcé de se dépatouiller avec pour seule indication : "Failed to specialize function template 'disable_if<is_same<T,Error>,std::string>::type to_string(const T &)'"...

    Vivement le static_assert du C++0x !

  5. #5
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Sinon, dans Loki, Boost & autres, il y a des static_asserts. Tu dois pouvoir en trouver un par trop compliqué à intégrer

  6. #6
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Bah le static_assert c'est pas non plus un truc énorme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template<bool> struct compileTimeErreur;
    template<> struct compileTimeErreur<true> {};
     
    #define STATIC_ASSERT(expr) (compileTimeError< expr != 0>())
    Bon le message sera plutôt ugly, Alexandrescu fait un truc plus poussé où tu passes le message d'erreur à sortir et il le rajoute au nom d'une classe juste pour ça, mais y'a un ou deux trucs qui m'ont un peu dérangé dans la syntaxe, du coup jm'en souviens pas.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  7. #7
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Ah mais je te parlais d'un static_assert qui affiche un message d'erreur. Et effectivement, j'ai le souvenir que c'est tordu

  8. #8
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    puisque tu me cherches :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<bool> struct compileTimeErreur;
    template<> struct compileTimeErreur<true> {};
     
    #define STATIC_ASSERT(expr, msg) \
         { CompileTimeErreur<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
    C'est le cast qui m'a toujours dérangé...
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  9. #9
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Je préfère ça

    Et effectivement, intéressant le cast. Quelqu'un sait pourquoi ?

  10. #10
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Je viens de faire des tests, que le cast en void soit là où pas on a toujours le même message d'erreur et le même comportement.
    Mais je vois mal Alexandrescu mettre une ligne inutile.

    Ah et soit dit en passant la version du bouquin est aussi sexy bien comme il faut :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <bool> struct compileTimeError {
        compileTimeError(...);
    };
     
    template <> compileTimeError<false> {};
     
    #define STATIC_ASSERT(expr, msg) \
    {\
        class ERROR_##msg; \
        (void)sizeof(compileTimeError< (expr) != 0>((ERROR_##msg())));\
    }
    perso y'a encore quelques petits trucs qui me posent soucie là =).
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  11. #11
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Le dernier exemple un peu tortueux mis à part. après quelques tests j'en viens a la conclusion (avec alp) que le cast en void après est un workaround pour certains compilos.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  12. #12
    Invité
    Invité(e)
    Par défaut
    C'est le cas, c'est quelque chose qu'on rencontrait pas mal dans les fonctions utilisant des asserts, typiquement des fonctions de test de pointeurs telles que

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    static void testeptr(P ptr)
    {
    assert(ptr);
    }
    une fonction de ce type renverra un message "unused parameter" à la compilation en mode release. Pour éviter cela, on lui ajoute

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    static void testeptr(P ptr)
    {
    assert(ptr);
    (void)ptr;
    }
    Le cast en void est nécessaire pour éviter des messages relatifs au fait que l'instruction ptr; ne fait rien... Je pense que c'est à peu près la même chose ici: si on passe msg comme paramètre à la fonction qui utilise STATIC_ASSERT.

    Le sizeof() du second exemple de Goten fait exactement la même chose... : il garantit que msg est utilisé, et présente peut être l'avantage additionnel qu'il ne sera pas supprimé par un analyseur statique qui fait du zèle... (le (void) est là pour éviter un grognement sur le fait qu'on n'en utilise pas la valeur).

    Le but du jeu, c'est que les assert disparaissent vraiment en mode release, s'ils génèrent des messages, c'est raté!

    Francois

  13. #13
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Oki doki merci pour la précision.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

Discussions similaires

  1. Réponses: 1
    Dernier message: 12/11/2013, 13h22
  2. std::bind sur une fonction d'une classe de base template
    Par Iradrille dans le forum Langage
    Réponses: 2
    Dernier message: 30/08/2013, 17h03
  3. Pointeur sur une fonction membre templatée et +
    Par metagoto dans le forum Langage
    Réponses: 2
    Dernier message: 09/08/2013, 02h05
  4. restriction sur une classe template
    Par Math75 dans le forum Langage
    Réponses: 31
    Dernier message: 02/10/2009, 11h02
  5. Pointeur sur une fonction template
    Par Progs dans le forum Langage
    Réponses: 2
    Dernier message: 15/02/2006, 20h25

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