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

C++ Discussion :

Ajouter des garanties à istream


Sujet :

C++

  1. #1
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut Ajouter des garanties à istream
    Bonjour,

    Les opérations sur les stream (notamment opérateur >>) ne donnent qu'une garantie basique. Ce que je voudrai, c'est créer une fonction qui garantie que soit l'opération réussi, soit l'opération rate et aucun caractère est extrait et l'objet n'est pas modifié. En gros, faire quelque chose du genre :

    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
    template<typename T>
    std::istream& safe_extract(std::istream& in, T& o)
    {
       T copy = o; //Assume T copyable
       auto state = SAVE_STREAM_INFORMATION(in);//Je ne sais pas comment faire, et je ne sais même pas si c'est possible...
       try
       {
           if(in>>o)
               return in;
           o = copy; //On suppose que T::operator= est nothrow pour le moment
           RESTORE(in, state); //Je ne sais pas comment écrire la fonction RESTORE
           return in;
       }
       catch(...)
       {
            o = copy; //On suppose que T::operator= est nothrow pour le moment
            RESTORE(in, state); //Je ne sais pas comment écrire la fonction RESTORE
            throw ;
       }
    }
    Parmi les idées que j'ai eues, il y a sauvegarder le buffer associé et essayer de le restaurer, mais j'ai réalisé que j'ai aucune garantie de ne pas avoir des nouveaux caractères qui sont arrivés dans le stream entre temps (et je veux les conserver). Est-ce possible de faire ce que je veux (les performances ne sont pas importantes) ? Si oui, comment ?

    Note : Si en plus restore pouvait être nothrow, ce serait parfait

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

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Les streams du standard ne sont peut-être pas les plus adaptés et une question me vient, dans quel but ? Parce que si une donnée n'est pas du bon type, le reste n'est probablement pas mieux.

    2 propositions:

    - Une qui fonctionne bien pour les types scalaires:

    Récupérer le "mot" et le convertir avec strtol et co.
    Ce que je nomme un mot est une suite de caractère non séparé par des espaces (cin >> std_string).
    Si échec, réinjecter le mot avec std::istream::unget.

    Gros problème cependant, unget() peut échouer.

    - Court-circuiter le streambuf (std::istream::rdbuf) du flux pour mettre le sien

    Et tu vas pleurer .

    Il faudrait modifier le streambuf du flux que tu vas traiter pour empêcher un échec de unget(). Un safebuf qui utiliserait n'importe quel streambuf et qui sauvegarde les caractères consommés.
    Donc la fonction safe_extract ne suffira pas (dû moins, pas toujours).

    Gros problème, safe_extract ne peut pas savoir le type du streambuf, il faudra le prévoir (prérequis ? possibilité que cela échoue ou que la fonction change le streambuf (comment le savoir ? dynamic_cast ?)).

    Au niveau du safebuf, il faut un mécanisme pour dire qu'une donnée est bien traitée et qu'il n'y a plus besoin de la sauvegarder, une espèce de lock/unlock par exemple que safe_extract utiliserait.

    En disant cela, je pense que safe_extract ne devrait pas prendre de istream mais une classe spécifique qui utilise un streambuf (fournit par l'utilisateur directement ou à travers un istream).

  3. #3
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Les streams du standard ne sont peut-être pas les plus adaptés et une question me vient, dans quel but ? Parce que si une donnée n'est pas du bon type, le reste n'est probablement pas mieux.
    Oui, les flux standards ne sont probablement pas ce qu'il y a de plus adapté il semblerait. Une utilisation est de pouvoir parser une grammaire LL(n) par exemple au lieu de LL(0) (ou LL(1) avec certaines définitions). Alors certes, usuellement pour parser, on récupère la chaine et ensuite on parse, mais pour des choses simples, on peut vouloir le faire directement depuis le flux. Ou encore, l'opération >> rate à cause d'une erreur réseau/accès fichier temporaire, mais le stream reste valide. Il serait agréable de pouvoir faire comme si rien ne s'était passé... En tout cas, j'arrive à voir des utilisations^^ Dans mon cas, c'est la première :

    Par exemple la grammaire pour une note est :

    N := Base Accidental
    Base := A | B | C ... G
    Accidental := eps | # | b


    Donc quand je lis une note, je lis deux caractères. Mais j'aimerai bien que quand j'ai la chaine AB, je puisse remettre le B (bon, ceci n'est pas un exemple d'échec, mais l'idée peut être la même avec un échec...)

    Gros problème cependant, unget() peut échouer.
    Oui, c'est exactement ce que j'avais remarqué quand j'avais essayé de trouver une solution...

    Court-circuiter le streambuf (std::istream::rdbuf) du flux pour mettre le sien
    Exactement ce que je n'avais pas envie de faire...


    Si ce n'est pas possible simplement, je vais probablement faire tout ça en parsant directement la chaine. Mais c'est assez embêtant puisque la chaine m'arrive progressivement par le réseau (ça m'oblige à avoir un buffer temporaire pour les caractères consommés mais pas encore utilisés).

    Au niveau du safebuf, il faut un mécanisme pour dire qu'une donnée est bien traitée et qu'il n'y a plus besoin de la sauvegarder, une espèce de lock/unlock par exemple que safe_extract utiliserait.
    Je pensais plutôt laisser à la responsabilité de l'utilisateur de remettre les caractères qu'il veut (ce ne serait pas à la classe de sauvegarder les données).

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

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Avec une LL(0) il n'y aurait aucun problème, unget n'échoue pas pour 1 caractère ^^.

    Si je ne me trompe pas, le parseur ne fait que 2 opérations: get et unget ?

    Dans ce cas, une petite structure comme celle-ci:

    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
    struct {
      std::strembuf * sbuf;
      std::stringbuf strbuf;
     
      std::stringbuf * pbuf = sbuf;
     
      using traits = std::stringbuf::traits;
     
      int_type get() {
        auto ic = pbuf->sbumpc();
        if (!traits::eq_int_type(ic, traits::eof)) {
          if (pbuf == &strbuf) {
            pbuf = sbuf;
            ic = pbuf->sbumpc();
          }
        }
        return ic;
      }
     
      void putback(char_type c) {
        auto ic = pbuf->sungetc(c);
        if (!traits::eq_int_type(ic, traits::eof)) {
          if (pbuf == sbuf) {
            char s[] = {c, '\0'};
            strbuf.str(s);
            pbuf = &strbuf;
          }
          else {
            strbuf.str(c + strbuf.str());
          }
        }
      }
    };
    (Écrit directement, pas testé)

    Les caractères qui ne peuvent pas être remis dans le flux sont injectés dans un stringbuf qui sert de tampon. En lecture, dès que stringbuf est vide, on revient sur le streambuf d'origine.
    Il peut être intéressant de faire une fonction pour injecter plusieurs caractères d'un coup et changer le stringbuf par quelque chose de plus efficace pour l'insertion au début, mais l'idée est là.

Discussions similaires

  1. DirectSound et le streaming
    Par Shakram dans le forum DirectX
    Réponses: 57
    Dernier message: 09/06/2005, 11h05
  2. [BLOB]Enreg Stream dans Field
    Par sbeu dans le forum Bases de données
    Réponses: 2
    Dernier message: 22/03/2004, 16h06
  3. Streaming video sous Linux
    Par freeshman dans le forum Applications et environnements graphiques
    Réponses: 2
    Dernier message: 03/01/2004, 17h17
  4. Streaming fichier PDF
    Par rgarnier dans le forum XMLRAD
    Réponses: 4
    Dernier message: 22/05/2003, 22h14
  5. Comment enregistrer un stream de longueur fixe ?
    Par Alcarbone dans le forum MFC
    Réponses: 5
    Dernier message: 13/04/2003, 20h14

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