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 :

Comment definir un range de valeur pour un type precis


Sujet :

Langage C++

  1. #1
    Membre expert
    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 344
    Points
    3 344
    Par défaut Comment definir un range de valeur pour un type precis
    Je definis parfois des types a semantique de valeur dont les valeurs ont un range valid predetermine.
    Par exemple, j'ai une class Port (en gros comme un port de protocol reseau, mais dans un cadre different et totalement pas pareil).
    J'ai definis Port pour que:

    1. Port() est invalid.
    2. Port(0) a Port(8) sont valid, tout autre valeure est invalide (pour l'instant j'assert mais je pourrais lancer une exception peut etre...).
    3. port++, ++port, --port, port-- sont definis et font les iterations logiquement, et peuvent donc resulter en des valeurs invalides.
    4. bool is_valid( const Port& port ) qui retourne true si le port a une valeur dans les valeurs valides.

    Je voudrais pouvoir ecrire (en c++11 compatible avec VS2012):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for( Port port : valid_ports_range() )
    {
         // utilise port
    }
    Ma question est: est-ce qu'il y a un moyen simple et court de definir valid_port_range()?

    Apparemment, la definition du range dans ce cas est serieusement relou.
    Ou alors il y a un truc simple que je vois pas, et du coup je m'en remet a votre experience.

    Note: je vois bien comment definir ce range, c'est vraiment une version courte que j'aimerai conaitre.

  2. #2
    Membre averti
    Homme Profil pro
    Cadre informatique
    Inscrit en
    Avril 2013
    Messages
    183
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Cadre informatique

    Informations forums :
    Inscription : Avril 2013
    Messages : 183
    Points : 435
    Points
    435
    Par défaut
    Bonjour,

    J'ai un petit souci de compréhension au niveau de ta question
    Que veux-tu définir par range? C'est le nombre de port possible et valide?

  3. #3
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    En gros un range c'est une pair d'iterateurs, le begin pointe vers la premiere valeur ou sur end, tandis que end est toujours invalid.
    De cette maniere on peut facilement parcourir une suite de valeur precise, en prenant begin et en le comparant a end a chaque iteration pour savoir si on a fini.

    Au cas ou tu ne sais pas, un iterateur est un "concept" (il y a des variantes) d'un type qui corresponds a une sorte de tete de lecture. Un pointeur est un iterateur par exemple. Tous les conteneurs de la bibliotheque standard utilisent des iterateurs.

    En gros, la solution "normale" a mon probleme reviens a quelque chose qui ressemblerait a ca, de maniere tres schematique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class valid_ports_range
    {
        static const std::array<Port, 8> valid_values = { 0, 1, 2, 3, 4, 5, 6, 7, 8}; //pas valid dans VS2012 malheureusement mais C++11 valide
    public:
        typedef decltype(valid_values)::iterator Iterator;
     
        Iterator begin() { return valid_values.begin(); }
        Iterator end() { return valid_values.end(); }
     
    };
    Ici tu peux penser a Iterator comme si c'etait Port*.
    Avec ca je peux repondre a ma question, parceque for-range-loop marche avec n'importe quoi qui fournis une fonction begin(), une fonction end() et que les deux retournent des iterateurs.

    Mais le probleme c'est qu'a chaque definition de nouvelles valeur de range je dois refaire la meme chose et ca m'enerve. En plus ca prends de la memoire pour rien.
    Une variante serait de definir un iterateur qui agis comme une monade de maniere a "generer" les differentes valeurs, suivant un algorithm si possible. Mais le code necessaire pour faire ce genre d'iterateur est long et difficile a rendre toujours correct (d'apres le standard). En plus meme une fois l'iterateur dispo, il faut en plus avoir un type qui fournis begin() et end().

    Donc, je me demande si il y a pas un racourcis generique pour definir des ranges qui soit plus pratique que ca.

    EDIT> Il y a une erreur dans le bout de code que j'ai fournis, saurez vous la trouver?

  4. #4
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Si tu définissais un opérateur * et un operator= à ta classe Port, alors Port serait un itérateur sur Port, non ? Est que ça ne simplifierait pas ton problème ?

    L'erreur, c'est le const_iterator ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  5. #5
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Bonjour,

    Est-ce que quelque chose comme ceci pourrait t'aider ?
    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
     
    #include<iostream>
     
    #include <boost/range/adaptor/transformed.hpp>
    #include <boost/range/irange.hpp>
     
    using namespace boost::adaptors;
    using namespace boost;
     
    struct Port
    {
        Port(int i) : i(i) {}
        int i;
    };
     
    struct
    {
        typedef Port result_type;
        Port operator()(int i) const { return Port(i); }
    } create_port;
     
    int main()
    {
        for(auto&& p : irange(0,9) | transformed(create_port))
            std::cout << p.i;
    }

  6. #6
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Si tu définissais un opérateur * et un operator= à ta classe Port, alors Port serait un itérateur sur Port, non ? Est que ça ne simplifierait pas ton problème ?
    Est-ce que c'est viable sur le long terme ce genre de technique? Ca ressemble a un hack mais je peu tenter le coup.

    L'erreur, c'est le const_iterator ?
    J'y ai meme pas pense

    En fait l'erreur c'est que j'ai mis plus de valeurs que l'array peut contenir. D'ailleurs, j'ai dis que Port(8) est cense etre valide mais c'est incorrect, ca va de 0 a 7, soit 8 valeurs valides.

  7. #7
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Bonjour,

    Est-ce que quelque chose comme ceci pourrait t'aider ?
    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
     
    #include<iostream>
     
    #include <boost/range/adaptor/transformed.hpp>
    #include <boost/range/irange.hpp>
     
    using namespace boost::adaptors;
    using namespace boost;
     
    struct Port
    {
        Port(int i) : i(i) {}
        int i;
    };
     
    struct
    {
        typedef Port result_type;
        Port operator()(int i) const { return Port(i); }
    } create_port;
     
    int main()
    {
        for(auto&& p : irange(0,9) | transformed(create_port))
            std::cout << p.i;
    }
    C'est vraiment tres moche mais si c'est encapsulable et generalizable alors ca pourrait aider.

  8. #8
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    A part le foncteur polymorphique (qui est lourd), je vois pas trop ce qui est "moche" (le C++14 devrait avoir des lambda polymorphiques et plus de chose sur la notion de range). Si c'est la notation que te dérange (les |), tu peux toujours utiliser les formes parenthésées .

    Sinon oui c'est encapsulable, et pour le "généralisable", ça dépend de ta problématique générale .

  9. #9
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Sinon oui c'est encapsulable, et pour le "généralisable", ça dépend de ta problématique générale .
    1. pouvoir generer un range avec differents types
    2. un moyen de specifier les valeurs valides (et pas forcement liees)

    Si vous avez encore des suggestions je suis preneur, je vais experimenter tout ca.

  10. #10
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Klaim Voir le message
    Est-ce que c'est viable sur le long terme ce genre de technique? Ca ressemble a un hack mais je peu tenter le coup.
    Je ne sais pas trop, c'est ce qui m'est venu sur le coup... En fait, j'ai eu le sentiment que ton Port n'était un véritable objet, mais un moyen d’accéder à un objet système sous-jacent. Et qu'en fait, tu ne voulais pas itérer sur des ports, mais te servir d'un Port pour itérer sur ces objets. D'où l'idée de faire de ton Port un itérateur. Mais je suis peut-être à côté de la plaque.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  11. #11
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    1/ C'est à dire un range basé sur PortA et un autre sur PortB par exemple ? Ou mélanger différent types au sein du même range ? Dans le premier cas, c'est faisable, je regarderais dans une autre direction que boost::range pour le second cas.

    2/ Donc dire c'est valide pour 1,2,3,6,8,15 sans forcément que ce soit valide pour tout les nombres de a à b ? Oui c'est possible, il suffit de changer le irange par un conteneur initialisé avec l'initializee-list que tu veux. J'ai utilisé irange parce que je pensais que ta problématique était vraiment de générer des range basé sur un intervalle de nombre .

  12. #12
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Je ne sais pas trop, c'est ce qui m'est venu sur le coup... En fait, j'ai eu le sentiment que ton Port n'était un véritable objet, mais un moyen d’accéder à un objet système sous-jacent. Et qu'en fait, tu ne voulais pas itérer sur des ports, mais te servir d'un Port pour itérer sur ces objets. D'où l'idée de faire de ton Port un itérateur. Mais je suis peut-être à côté de la plaque.
    Port, dans mon cas reel, est vraiment ce que je decris, un ensemble de valeur fixes, type pour differentes raisons de sanite mentale autant dans l'utilisation de ces valeurs que lors de la serialization. Port est utilise dans des context precis dans mon jeu, et j'empeche la possibilite d'autres contextes en ne faisant pas de conversion explicite vers, par exemple, int (en interne Port est un unsigned char).
    Donc je dirais que c'est pas un systeme sous jascent mais bien une valeur dans un ensemble definis.

    Ce qui me fait penser qu'il y a aussi boost constrained-value mais je me demande si c'est pas un peu overkill.
    Je vais voir ca.

    Ta solution ressemble a un hack qui permet de pas avoir a definir un type suplementaire. De loin ca dois marcher, mais comme la valeur et l'iterateur sont de meme type, j'ai jamais essaye voir si ca passe dans tous les cas ou tu passes un iterateur. Enfin theoriquement ca dois passer.

  13. #13
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    1/ C'est à dire un range basé sur PortA et un autre sur PortB par exemple ? Ou mélanger différent types au sein du même range ? Dans le premier cas, c'est faisable, je regarderais dans une autre direction que boost::range pour le second cas.
    Je voulais dire un type de range pour Foo, un autre pour Bar, etc. Genre MyRange<Foo>, MyRange<Bar>...

    2/ Donc dire c'est valide pour 1,2,3,6,8,15 sans forcément que ce soit valide pour tout les nombres de a à b ? Oui c'est possible, il suffit de changer le irange par un conteneur initialisé avec l'initializee-list que tu veux. J'ai utilisé irange parce que je pensais que ta problématique était vraiment de générer des range basé sur un intervalle de nombre .
    Ok, merci je vais voir les algorithmes, j'etais pas au courant de cet lib dans boost.

  14. #14
    Membre éclairé Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Points : 845
    Points
    845
    Par défaut
    A première vue je ferai quelque chose proche de ceci:

    valid_ports_range() retourne (ou est) un "iterator_proxy", c'est à dire très proche de ton exemple dans ton 2nd post.

    Par contre begin() et end() retourneraient un custom iterator, objet dérivant de std::iterator avec comme category std::bidirectional_iterator_tag (ou simplement std::forward_iterator_tag, à toi de voir).
    Il faudra donc implémenter les opérations associées à la category (operator++, dereference etc).
    Tu peux faire varier le value_type du custom iterator lorsqu'il est instancié par le proxy (begin() vs cbegin() par exemple) et tu peux te contenter de passer les bornes supérieure et inférieure à ton custom iterator (op ++ ou dereference n'agissent que sur un simple compteur interne).

    Pour grandement simplifier la définition du custom iterator, il y a Boost.Iterator (iterator facade and adaptor)

  15. #15
    Membre expert
    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 344
    Points
    3 344
    Par défaut
    Citation Envoyé par metagoto Voir le message
    A première vue je ferai quelque chose proche de ceci:

    valid_ports_range() retourne (ou est) un "iterator_proxy", c'est à dire très proche de ton exemple dans ton 2nd post.

    Par contre begin() et end() retourneraient un custom iterator, objet dérivant de std::iterator avec comme category std::bidirectional_iterator_tag (ou simplement std::forward_iterator_tag, à toi de voir).
    Il faudra donc implémenter les opérations associées à la category (operator++, dereference etc).
    Tu peux faire varier le value_type du custom iterator lorsqu'il est instancié par le proxy (begin() vs cbegin() par exemple) et tu peux te contenter de passer les bornes supérieure et inférieure à ton custom iterator (op ++ ou dereference n'agissent que sur un simple compteur interne).
    C'est exactement le genre de complexite que je veux eviter d'avoir a faire, surtout que je risque de faire ca regulierement.
    (C'est aussi les premieres solutions auquelles je pensais)

    Pour grandement simplifier la définition du custom iterator, il y a Boost.Iterator (iterator facade and adaptor)
    Je regarde ca demain mais je crains que ca ne soit un trop gros bazooka.

  16. #16
    Membre éclairé Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Points : 845
    Points
    845
    Par défaut
    Citation Envoyé par Klaim Voir le message
    C'est exactement le genre de complexite que je veux eviter d'avoir a faire, surtout que je risque de faire ca regulierement.
    (C'est aussi les premieres solutions auquelles je pensais)
    A mon avis, tu n'auras pas plus simple et générique que ça.

    D’après le nom valid_ports_range, on s'attend à travailler avec les fameux objets Port.
    En revanche le "custom" iterator dont j'ai parlé peut lui être très générique et être utilisé pour beaucoup d'autres de tes objets "à range" (si j'ai bien compris).
    Et Il y a toujours la possibilité de le spécialiser pour des value_type et/ou des ranges speciaux. Voir même passer les ranges en tant que "valuelist" (un typelist de valeurs intégrales si connues à la compilation).

    Implémenter un input ou un forward_iterator n'est pas si lourd.

Discussions similaires

  1. Réponses: 4
    Dernier message: 04/10/2010, 11h02
  2. Comment definir une range à colonne variable ?
    Par daduck dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 21/07/2008, 10h09
  3. Comment représenter des intervalles de valeurs pour un champ?
    Par dedelem dans le forum Modélisation
    Réponses: 1
    Dernier message: 14/04/2008, 17h50
  4. [Upload] récupération de valeur pour un type "file"
    Par carusier dans le forum Langage
    Réponses: 4
    Dernier message: 28/03/2007, 17h44
  5. Réponses: 2
    Dernier message: 20/10/2006, 08h23

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