Fonction de saisie sécurisé

Parce que vérifier à chaque fois std::cin...

a marqué ce sujet comme résolu.

Bonjour,

J'ai réalisé une fonction pour sécuriser les saisies en console. J'ai essayé qu'elle soit la plus générique possible. Il y a en fait 4 fonctions, leur déclaration :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
template<typename T>
bool sInStream(std::istream &stream, T &data);

template<typename T, typename Function>
bool sInStream(std::istream &stream, T &data, Function const& function);

//Spécialisations pour std::string :

template<>
bool sInStream<std::string>(std::istream &stream, std::string &str);

template<typename Function>
bool sInStream(std::istream &stream, std::string& str, Function const& function);~~~

La fonction prend en prend en paramètre un flux qui hérite de std::istream, la variable dans laquelle sera stockée la saisie et, optionnel, un prédicat qui vérifie que la valeur saisie respecte bien certaines contraintes. Si la fonction possède une spécialisation pour le type std::string, c'est parce qu'elle utilise std::getline() en interne. La fonction renvoie true si tout c'est bien déroulé et si le prédicat renvoie true aussi. Sinon la fonction renvoie false. Cela permet de l'utiliser dans une boucle.

Si je la poste ici c'est pour que vous me donniez votre avis, si c'est une bonne solution ou si la fonction n'est en fait pas du tout sécurisée, si c'est mal conçus,… bref, vos conseils. Merci d'avance !

Le 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
#ifndef S_IN_STREAM_H
#define S_IN_STREAM_H

#include <iostream>  
#include <limits>
#include <string>

template<typename T, typename Function>
bool sInStream(std::istream &stream, T &data, Function const& function) {
    if (!(stream >> data)) {
        stream.clear();
        stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return false;
    }

    if (function(data)) {
        stream.clear();
        stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return true;
    }

    else
        stream.clear();
        stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return false;
};

template<typename T>
bool sInStream(std::istream &stream, T &data) {
    if (!(stream >> data)) {
        stream.clear();
        stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return false;
    }

    stream.clear();
    stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    return true;
};

template<>
bool sInStream<std::string>(std::istream &stream, std::string &str) {
    std::getline(stream, str);

    stream.clear();
    stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    return true;
};


template<typename Function>
bool sInStream(std::istream &stream, std::string& str, Function const& function) {
    std::getline(stream, str);

    stream.clear();
    stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    if (function(str)) {
        return true;
    }

    else
        return false;
};

#endif //S_IN_STREAM_H

Et le code de test :

 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 "sInStream.h"
#include <string>

bool foo(const float fl) {
    return fl >= 0.f && fl <= 1.f;
};

int main() {
    auto uint = 0u;
    do {
        std::cout << "Entrez un nombre entier entre 1 et 10\n";
    } while (!sInStream<unsigned int>(std::cin, uint, [](int var) {return var > 1 && var < 10; }));

    float fl{};
    do {
        std::cout << "Entrez un à virgule nombre entre 0 et 1\n";
    } while (!sInStream<float>(std::cin, fl, foo));

    std::string str{};
    do {
        std::cout << "Entrez votre nom :\n";
    } while (!sInStream<std::string>(std::cin, str));

    std::cout << "uint = " << uint << "\nfl = " << fl << "\nVous vous appellez " << str << "\n";
    std::cin >> uint;
}

Merci encore et A++ ;)

Merci pour vos réponse.

@Ksass`Peuk : J'étais déjà tombé sur ce code @ZeFresk en parcourant le forum d'OC (mais je l'avais perdu :p ). Je vais essayer d'explorer son code plus en profondeur.

@Imghs : Je vais me renseigner sur comment fermer un flux en ce qui concerne,

si tout se passe bien, il ne faut pas nettoyer le flux et purger ce qu'il y reste.

C'est ce que je m'étais dit mais dans le code de test, quand je tapais "6.5" au premier appel (nombre entier entre 1 et 10), la fonction extrayait le "6", laissait le ".5" et lors de l'appel suivant (nombre à virgule entre 0 et 1) extrayait ce ".5". Donc du coup je préfère nettoyer dans tous les cas. Après, comme on me l'a fait remarquer, dans ce cas dans ce cas la fonction doit carrément demander à l'utilisateur de ressaisir et non interpréter 6.5 comme étant 6… Mais je ne vois pas très bien comment faire.

Et que veux tu dire par :

Et plus je pense au sujet, plus que je mis que les fonctions simplificatrices sur le sujet complexifie la chose.

Ce n'est pas toi qui va fermer le flux. Il te sera fermé sous le nez, et il faut savoir le prendre en compte. D'où le code que j'ai donnée dans la FAQ.

Ce que je veux dire, c'est que je trouve les fonctions qui doivent soit disant simplifier faire tout le contraire. Surtout que plusieurs cas doivent être prévus à l'issue de la "factorisation": - si fermeture du flux, => arrêt prochain du programme (une fois les entrées traitées) - si erreur de saisie alors au choix une demande pour recommencer, ou un arrêt.

J'aurai tendance à regarder dans les évolutions de la SL avec les ranges par Eric Niebler pour m'inspirer pour fournir ce genre de fonction qui prendrait en paramètre lambda ce qui va bien pour recommencer.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte