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 :

to_string template pour conteneur [c++11/c++14]


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de Suryavarman
    Homme Profil pro
    Développeur 3D
    Inscrit en
    Mai 2006
    Messages
    233
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur 3D
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Mai 2006
    Messages : 233
    Par défaut to_string template pour conteneur [c++11/c++14]
    Bonjour à toutes et à tous


    Je suis en train de me faire une fonction pour convertir en chaîne de caractères les objets de mon api. J'utilise c++14. Mais si vous avez une solution en c++17 ou c++20 n'hésitez pas à la proposer car même avec ces normes je ne vois pas comment faire :p.
    Du coup j'ai des nombres, des pair, des conteneurs (je ne mets pas les tuples pour le moment mais si ça intéresse quelqu'un d'itérer sur un tuple -> https://stackoverflow.com/questions/...dtuple-in-c-11)


    L'idée est d'appeler to_string avec un objet ou un nombre en paramètre et d'avoir en retour leur représentation en chaîne de caractères. La représentation dans l'exemple est un exemple simple pour développer l'algorithme.

    J'ai trouvé une solution pour les conteneurs mais ça m'oblige à définir un template pour chaque type de conteneur.
    J'aurais bien aimé une seule fonction qui chapeaute tout les conteneurs.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    template <typename T_Container>
    std::string to_string(const T_Container& value) 
    {
        std::stringstream ss;  
        ss << "{" ;
     
        for(auto it : value)    
            ss << to_string(*it);
     
        ss << "}";
        return ss.str();
    }

    Le problème c'est qu'elle ne permet pas au compilateur de la différencier de la fonction pour les types simples:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <typename T>
    std::string to_string(T value) 
    {
        std::stringstream ss;
        ss.precision(std::numeric_limits<T>::digits10);
        ss << "T: " << value;
        return ss.str();
    }
    Auriez-vous une idée ?

    Un grand merci.

    Voici le code.

    Lien vers coliru pour éditer le code:
    https://coliru.stacked-crooked.com/a/c3e36f81070384ea
    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
     
    #include <iostream>
    #include <sstream>
    #include <limits>
    #include <map>
     
    template <typename T>
    std::string to_string(T value) 
    {
        std::stringstream ss;
        ss.precision(std::numeric_limits<T>::digits10);
        ss << "T: " << value;
        return ss.str();
    }
     
    template <>
    std::string to_string(long value) 
    {
        std::stringstream ss;
        ss.precision(std::numeric_limits<long>::digits10);
        ss << "long: " << value;
        return ss.str();
    }
     
    template <typename T1, typename T2>
    std::string to_string(const std::pair<T1, T2>& value) 
    {
        std::stringstream ss;
        ss << "{" << to_string(value.first) << ", " << to_string(value.second) << "}";
        return ss.str();
    }
     
    /*
    template <typename T_Container>
    std::string to_string(const T_Container& value) 
    {
        std::stringstream ss;  
        ss << "{" ;
        
        for(auto it : value)    
            ss << to_string(*it);
        
        ss << "}";
        return ss.str();
    }
     
    /*/
    template <typename ... Ts>
    std::string to_string(const std::map<Ts...>& value) 
    {
        std::stringstream ss;  
        ss << "{" ;
     
        for(auto it : value)    
            ss << to_string(it);
     
        ss << "}";
        return ss.str();
    }
    //*/
     
    int main()
    {
        long toto_long = 1;
        float toto_float = 2.3f;
        std::pair<long, float> toto_pair = {toto_long, toto_float};
        std::map<long, float> toto_map_less = {{toto_long, toto_float}, {toto_long*2, toto_float}};
        std::map<long, float, std::greater<long>> toto_map_greater = {{toto_long, toto_float}, {toto_long*2, toto_float}};
     
        std::cout << to_string(toto_long) << " " << to_string(toto_float) << std::endl;    
        std::cout << to_string(toto_pair) << std::endl;    
        std::cout << to_string(toto_map_less) << std::endl;
        std::cout << to_string(toto_map_greater) << std::endl;
    }
    Le résultat attendu:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    long: 1 T: 2.3
     
    {long: 1, T: 2.3}
     
    {{long: 1, T: 2.3}{long: 2, T: 2.3}}
     
    {{long: 2, T: 2.3}{long: 1, T: 2.3}}

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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 : 1 599
    Par défaut
    Bonjour,

    Il te faut utiliser le SFINAE. Note qu'en C++20 avec les concept, il y une solution encore plus simple.
    On va créer un type de retour qui n'aura aucun sens si la première n'est pas iterable, et pour l'autre qui n'aura aucun sens si on ne peur pas l'utiliser avec stringstream
    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
    // notre type de retour, c'est std::string mais ça dépend des conditions, ici on impose un paramètre qui doit être un type existant
    template<typename> struct string_ { using type=std::string; };
     
    template <typename T_Container>
    typename string_<typename T_Container::const_iterator>::type  // n'a de sens que si on à un const_iterator
    to_string(T_Container const& value) {
        std::stringstream ss;
        ss << "{";
     
        for (auto const& it : value)
            ss << to_string(it);
     
        ss << "}";
        return ss.str();
    }
     
    template <typename T>
    typename string_<decltype(std::stringstream{}<<std::declval<T>())>::type // on doit pouvoir faire ss<<T
    to_string(const T& value) {
        std::stringstream ss;
        ss.precision(std::numeric_limits<T>::digits10);
        ss << "T: " << value;
        return ss.str();
    }
    Tes fonctions seront donc clairement différenciées. Et de plus la première fonctionne sur les collections mais aussi sur des flux.
    Mais si tu veux faire to_string(std::string("hello")) problème. Les 2 sont valides car on peut itérer dans une string et on peut convertir une string en une string. Alors où tu considères que de toute façon ça n'a pas de sens, où tu définis une troisième fonction qui gérera les std::string.

  3. #3
    Membre éclairé Avatar de Suryavarman
    Homme Profil pro
    Développeur 3D
    Inscrit en
    Mai 2006
    Messages
    233
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur 3D
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Mai 2006
    Messages : 233
    Par défaut
    Merci beaucoup.

    (Je ne vais pas avoir le temps de tester ça avant samedi soir.)

    Je ne connaissais pas le principe de SFINAE. (Si il y a un tutorial/faq sur developpez je suis preneur. J'ai fait une simple recherche j'ai pas trouvé)
    https://en.cppreference.com/w/cpp/language/sfinae

    Ça reste encore assé confu pour moi. Je vais relire ça plusieurs fois.

    Ce que je comprend pour le moment c'est que les templates que l'on défini ont un typename d'une certaine nature. Ce qui permet de le différencier un template d'un autre ayant la même signature de fonction.
    Du coup ça me résout d'autre problèmes. Comme par exemple une fonction bool IsValidString(const string& str) qui n'avait aucun type pour la différencier. Et avec la SFINAE ça me résoudra sûrement le soucis. Enfin c'est pour le moment ce que je comprend :p.


    J'en profite pour poser d'autres questions :

    1 - C'est nouveau depuis quelques années je vois de plus en plus des paramètres d'entrés qui s'écrivaient (const Toto& value), s'écrire dorénavant (Toto const& value)

    Je ne comprend pas bien la différence. Sinon pourquoi ne pas écrire const Toto const& value et s'assurer que tout soit constant?

    2 - J'ai vue que tu as mis const T&. Je suis curieux de te confronté mon résonnement. (J'en profite désolé)

    Hormis pour le cas de std::string et des conteneurs, je ne vais pas passer par référence mes nombres et pointeurs partagé. Pour les nombres le passage par copie est plus rapide que par référence (du moins c'était vrais il y a 6 ans). Pour mes pointeurs partagés le passage par référence arrive régulièrement à des surprises dans mon api. J'utilise un système d'observation qui peut réagir à une action et la référence sur le pointeur partagé peux ne plus être valide, (car le ref count n'aura pas été incrémenté car passé par référence).

    Du coup le passage par copie s'avère plus rapide pour les nombres et sécurisé pour les pointeurs partagés.

    D'ailleurs je me demandai si un pointeur partagé passé en copie n'est pas optimisé.

    Encore merci pour ta réponse.

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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 : 1 599
    Par défaut
    0-
    Le SFINAE permet d'effacer une fonction ou une classe template, si un paramètre du template ou la valeur de retour ou un paramètre de la fonction qui dépend d'un paramètre de template est incohérent. La fonction ou la classe est alors invisible.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T, class E=typename T::begin >  class X{};
     
    int main() {
        X<std::vector<int>>  a; // OK
        X<int>  b;              // erreur X<int> n'est pas une classe !
    }
    1-
    Les 2 sont équivalents. On peut écrire const et volatile avant ou après le mot désignant le type. Attention s'il y a plusieurs niveaux il n'y a qu'une seule possibilités pour les autres const. Et constexpr ou mutable doivent être toujours au début.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const T& x; <=> T const& x; // référence à un T constant
    const T* p; <=> T const* p; // pointeur non constant sur un T constant
    T* const p = nullptr;       // pointeur constant sur un T non constant
    const T* const p = nullptr; <=> T const* const p = nullptr; // pointeur constant sur un T constant
    constexpr T* p = nullptr;   // p est une constante de compilation sur un T non constant
    2-
    Oui le passage par référence est moins performant qu'une copie pour un type simple. Mais dans cas le d'une const référence le compilateur va optimiser et ça revient très souvent au même. J'ai mis une const référence car on n'est pas sûr que le type sera toujours simple ici, donc pour les types simples le compilateur optimisera et pour les types complexes il utilisera la const référence. Pour les template c'est recommandé d'éviter les passages par copie, la const référence est garantie de toujours fonctionner alors que la copie peut poser des problèmes (non optimal voire erreur sur objet non copiable.)

    Et si on passe un pointeur par copie, il n'y a pas d'optimisation la valeur du pointeur est directement copiée, on ne peut pas faire mieux.

  5. #5
    Membre éclairé Avatar de Suryavarman
    Homme Profil pro
    Développeur 3D
    Inscrit en
    Mai 2006
    Messages
    233
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur 3D
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Mai 2006
    Messages : 233
    Par défaut
    Un grand merci.

    Le code suivant est possible ?
    ça ne devrait pas être:

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Ce n'est pas en tatônnant la syntaxe qu'on arrive à un résultat.
    Ça suit une règle très simple
    - const s'applique à ce qui est à sa gauche
    - s'il n'y a rien à gauche, alors il s'applique à ce qui se trouve à sa droite
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    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 : 1 599
    Par défaut
    Citation Envoyé par Suryavarman Voir le message
    Un grand merci.

    Le code suivant est possible ?
    ça ne devrait pas être:
    Les 2 sont valides mais n'indiquent pas la même chose. Pour le détail je l'ai déjà écrit.

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

Discussions similaires

  1. [VisualStudio 2005 Pro] Template pour Console en C
    Par dorian833 dans le forum Visual C++
    Réponses: 2
    Dernier message: 21/02/2007, 11h27
  2. [Template] Utilisation de template pour l'envoi de mail
    Par eXiaNazaire dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 30/03/2006, 10h28
  3. [XSL] xsl:apply-templates pour les attribus
    Par luta dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 24/02/2006, 16h35

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