web-dev-qa-db-fra.com

Restaurer l'état de std :: cout après l'avoir manipulé

Supposons que j'ai un code comme celui-ci:

void printHex(std::ostream& x){
    x<<std::hex<<123;
}
..
int main(){
    std::cout<<100; // prints 100 base 10
    printHex(std::cout); //prints 123 in hex
    std::cout<<73; //problem! prints 73 in hex..
}

Ma question est de savoir s'il existe un moyen de "restaurer" l'état de cout à son état d'origine après le retour de la fonction? (Un peu comme std :: boolalpha et std :: noboolalpha ..)?

Merci.

88
UltraInstinct

tu dois #include <iostream> ou #include <ios> puis si nécessaire:

std::ios_base::fmtflags f( cout.flags() );

//Your code here...

cout.flags( f );

Vous pouvez les mettre au début et à la fin de votre fonction, ou consultez cette réponse sur la façon de l'utiliser avec RAII .

78
Stefan Kendall

Boost IO Stream State Saver semble exactement ce dont vous avez besoin. :-)

Exemple basé sur votre extrait de code:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}
56
Chris Jester-Young

Notez que les réponses présentées ici ne rétabliront pas l'état complet de std::cout. Par exemple, std::setfill "Collera" même après avoir appelé .flags(). Une meilleure solution consiste à utiliser .copyfmt:

std::ios oldState(nullptr);
oldState.copyfmt(std::cout);

std::cout
    << std::hex
    << std::setw(8)
    << std::setfill('0')
    << 0xDECEA5ED
    << std::endl;

std::cout.copyfmt(oldState);

std::cout
    << std::setw(15)
    << std::left
    << "case closed"
    << std::endl;

Imprime:

case closed

plutôt que:

case closed0000
37
rr-

J'ai créé une classe RAII en utilisant l'exemple de code de cette réponse. Le gros avantage de cette technique vient si vous disposez de plusieurs chemins de retour à partir d'une fonction qui définit des indicateurs sur un iostream. Quel que soit le chemin de retour utilisé, le destructeur sera toujours appelé et les drapeaux seront toujours réinitialisés. Il n'y a aucune chance d'oublier de restaurer les drapeaux lorsque la fonction revient.

class IosFlagSaver {
public:
    explicit IosFlagSaver(std::ostream& _ios):
        ios(_ios),
        f(_ios.flags()) {
    }
    ~IosFlagSaver() {
        ios.flags(f);
    }

    IosFlagSaver(const IosFlagSaver &rhs) = delete;
    IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;

private:
    std::ostream& ios;
    std::ios::fmtflags f;
};

Vous pouvez ensuite l'utiliser en créant une instance locale de IosFlagSaver chaque fois que vous souhaitez enregistrer l'état de drapeau actuel. Lorsque cette instance sort du domaine d'application, l'état du pavillon est restauré.

void f(int i) {
    IosFlagSaver iosfs(std::cout);

    std::cout << i << " " << std::hex << i << " ";
    if (i < 100) {
        std::cout << std::endl;
        return;
    }
    std::cout << std::oct << i << std::endl;
}
19
qbert220

Avec un peu de modification pour rendre la sortie plus lisible:

void printHex(std::ostream& x) {
   ios::fmtflags f(x.flags());
   x << std::hex << 123 << "\n";
   x.flags(f);
}

int main() {
    std::cout << 100 << "\n"; // prints 100 base 10
    printHex(std::cout);      // prints 123 in hex
    std::cout << 73 << "\n";  // problem! prints 73 in hex..
}
7
whacko__Cracko

Vous pouvez créer un autre wrapper autour du tampon stdout:

#include <iostream>
#include <iomanip>
int main() {
    int x = 76;
    std::ostream hexcout (std::cout.rdbuf());
    hexcout << std::hex;
    std::cout << x << "\n"; // still "76"
    hexcout << x << "\n";   // "4c"
}

Dans une fonction:

void print(std::ostream& os) {
    std::ostream copy (os.rdbuf());
    copy << std::hex;
    copy << 123;
}

Bien sûr, si les performances sont un problème, cela coûte un peu plus cher, car il copie l'intégralité de l'objet ios (mais pas le tampon), y compris certaines choses que vous payez, mais peu susceptibles d'utiliser telles que les paramètres régionaux.

Sinon, j'ai l'impression que si vous allez utiliser .flags() il vaut mieux être cohérent et utiliser .setf() également plutôt que la syntaxe << (Pure question de style) .

void print(std::ostream& os) {
    std::ios::fmtflags os_flags (os.flags());
    os.setf(std::ios::hex);
    os << 123;
    os.flags(os_flags);
}

Comme d'autres l'ont dit, vous pouvez mettre ce qui précède (et .precision() et .fill(), mais généralement pas les paramètres régionaux et les mots qui ne seront généralement pas modifiés et sont plus lourds) dans une classe pour plus de commodité et pour la rendre exceptionnelle; le constructeur doit accepter std::ios&.

4
n.caillou