Stellen Sie den Status von std :: cout wieder her, nachdem Sie ihn bearbeitet haben

105

Angenommen, ich habe einen Code wie diesen:

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..
}

Meine Frage ist, ob es eine Möglichkeit gibt cout, den ursprünglichen Zustand nach der Rückkehr von der Funktion wiederherzustellen . (Etwas wie std::boolalphaund std::noboolalpha..)?

Vielen Dank.

UltraInstinct
quelle
Ich glaube, Hex dauert nur für die nächste Auslagerungsoperation. Die Änderung bleibt nur bestehen, wenn Sie die Formatflags manuell ändern, anstatt Manipulatoren zu verwenden.
Billy ONeal
4
@BillyONeal: Nein, die Verwendung von Manipulatoren hat den gleichen Effekt wie das manuelle Ändern der Formatflags. :-P
Chris Jester-Young
3
Wenn Sie aufgrund eines Covertiy-Befundes hier sind , bei dem das Ostream-Format nicht wiederhergestellt wird (STREAM_FORMAT_STATE) , lesen Sie Coverity-Feststellung: Das Ostream-Format wird nicht wiederhergestellt (STREAM_FORMAT_STATE) .
JWW
Ich habe etwas Ähnliches getan - siehe meine Frage zur Codeüberprüfung: Verwenden Sie einen Standard-Stream und stellen Sie anschließend die Einstellungen wieder her .
Toby Speight
1
Diese Frage ist ein perfektes Beispiel dafür, warum iostream nicht besser als stdio ist. Ich habe gerade zwei böse Fehler gefunden, weil ich nicht / halb- / vollständig / was-nicht hartnäckig bin.
Fuujuhi

Antworten:

97

Sie müssen #include <iostream>oder #include <ios>dann, wenn erforderlich:

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

//Your code here...

cout.flags( f );

Sie können diese an den Anfang und das Ende Ihrer Funktion setzen oder diese Antwort zur Verwendung mit RAII lesen .

Stefan Kendall
quelle
5
@ ChrisJester-Young, eigentlich ist gutes C ++ RAII, besonders in einem Fall wie diesem!
Alexis Wilke
4
@Alexis Ich stimme zu 100% zu. Siehe meine Antwort (Boost IO Stream State Saver). :-)
Chris Jester-Young
3
Dies ist nicht ausnahmesicher.
Einpoklum
2
Neben den Flags gibt es im Stream-Status noch mehr.
JWW
3
Sie können das Problem vermeiden, indem Sie keine Formate auf Streams übertragen. Schieben Sie das Format und die Daten in eine temporäre Stringstream-Variable und drucken Sie sie dann aus
Mark Sherred
63

Der Boost IO Stream State Saver scheint genau das zu sein, was Sie brauchen. :-)

Beispiel basierend auf Ihrem Code-Snippet:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}
Chris Jester-Young
quelle
1
Beachten Sie, dass es hier keine Magie gibt, die im ios_flags_saverGrunde nur die Flags speichert und setzt, wie in der Antwort von @ StefanKendall.
Einpoklum
15
@einpoklum Aber es ist ausnahmsicher, im Gegensatz zu der anderen Antwort. ;-)
Chris Jester-Young
2
Neben den Flags gibt es im Stream-Status noch mehr.
JWW
4
@jww Die IO Stream State Saver-Bibliothek verfügt über mehrere Klassen zum Speichern verschiedener Teile des Stream-Status, von denen ios_flags_savernur eine ist.
Chris Jester-Young
3
Wenn Sie der Meinung sind, dass es sich lohnt, alles selbst neu zu implementieren und zu warten, anstatt eine überprüfte, gut getestete Bibliothek zu verwenden ...
jupp0r
45

Beachten Sie, dass die hier dargestellten Antworten nicht den vollständigen Status von wiederherstellen std::cout. Zum Beispiel std::setfillbleibt auch nach dem Anruf "hängen" .flags(). Eine bessere Lösung ist zu verwenden .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;

Wird drucken:

case closed

eher, als:

case closed0000
rr-
quelle
Obwohl meine ursprüngliche Frage vor einigen Jahren beantwortet wurde, ist diese Antwort eine großartige Ergänzung. :-)
UltraInstinct
2
@UltraInstinct Es scheint eine bessere Lösung zu sein. In diesem Fall können und sollten Sie es stattdessen zur akzeptierten Antwort machen.
underscore_d
Dies löst aus bestimmten Gründen eine Ausnahme aus, wenn Ausnahmen für den Stream aktiviert sind. coliru.stacked-crooked.com/a/2a4ce6f5d3d8925b
anton_rh
1
Es scheint, dass std::ioses immer in einem schlechten Zustand ist, weil es NULLrdbuf hat. Das Festlegen eines Status mit aktivierten Ausnahmen führt daher zum Auslösen von Ausnahmen aufgrund eines schlechten Status. Lösungen: 1) Verwenden Sie eine Klasse (zum Beispiel std::stringstream) mit rdbufset anstelle von std::ios. 2) Speichern Sie den Ausnahmestatus separat in der lokalen Variablen und deaktivieren Sie sie zuvor. Stellen Sie state.copyfmtdann die Ausnahme von der Variablen wieder her (und wiederholen Sie dies, nachdem Sie den Status wiederhergestellt oldStatehaben, für den Ausnahmen deaktiviert sind). 3) rdbufstd::iosstruct : std::streambuf {} sbuf; std::ios oldState(&sbuf);
Stellen
22

Ich habe eine RAII-Klasse mit dem Beispielcode aus dieser Antwort erstellt. Der große Vorteil dieser Technik besteht darin, dass Sie mehrere Rückgabepfade von einer Funktion haben, die Flags in einem Iostream setzt. Unabhängig davon, welcher Rückweg verwendet wird, wird der Destruktor immer aufgerufen und die Flags werden immer zurückgesetzt. Es besteht keine Chance zu vergessen, die Flags wiederherzustellen, wenn die Funktion zurückkehrt.

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;
};

Sie würden es dann verwenden, indem Sie eine lokale Instanz von IosFlagSaver erstellen, wann immer Sie den aktuellen Flag-Status speichern möchten. Wenn diese Instanz den Gültigkeitsbereich verlässt, wird der Flag-Status wiederhergestellt.

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;
}
qbert220
quelle
2
Hervorragend, wenn jemand wirft, haben Sie immer noch die richtigen Flaggen in Ihrem Stream.
Alexis Wilke
4
Neben den Flags gibt es im Stream-Status noch mehr.
JWW
1
Ich wünschte wirklich, C ++ erlaubt versuchen / endlich. Dies ist ein hervorragendes Beispiel, bei dem RAII funktioniert, aber letztendlich einfacher gewesen wäre.
Handelsideen Philip
2
Wenn Ihr Projekt zumindest ein bisschen vernünftig ist, haben Sie Boost und das kommt mit staatlichen Sparern für diesen Zweck.
Jan Hudec
9

Mit ein wenig Modifikation, um die Ausgabe besser lesbar zu machen:

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..
}
whacko__Cracko
quelle
9

Sie können einen weiteren Wrapper um den Standardpuffer erstellen:

#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"
}

In einer Funktion:

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

Wenn die Leistung ein Problem darstellt, ist dies natürlich etwas teurer, da das gesamte iosObjekt (aber nicht der Puffer) kopiert wird, einschließlich einiger Dinge, für die Sie bezahlen, die Sie jedoch wahrscheinlich nicht verwenden werden, wie z. B. das Gebietsschema.

Ansonsten denke ich, wenn Sie es verwenden wollen, ist .flags()es besser, konsistent zu sein und es auch zu verwenden, .setf()als die <<Syntax (reine Frage des Stils).

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

Wie andere gesagt haben, können Sie die oben genannten (und .precision()und .fill(), aber normalerweise nicht das Gebietsschema und wortbezogene Dinge, die normalerweise nicht geändert werden und schwerer sind) in eine Klasse einordnen, um sie zu vereinfachen und ausnahmsicher zu machen. Der Konstruktor sollte akzeptieren std::ios&.

n.caillou
quelle
Guter Punkt [+], aber er erinnert sich natürlich daran, ihn std::stringstreamfür den Formatierungsteil zu verwenden, wie Mark Sherred betonte .
Wolf
@ Wolf Ich bin nicht sicher, ob ich deinen Standpunkt verstehe. An std::stringstream ist ein std:ostream, außer wenn man eins verwendet, wird ein zusätzlicher Zwischenpuffer eingeführt.
n.caillou
Natürlich sind beide gültige Ansätze zum Formatieren der Ausgabe. Beide führen ein Stream-Objekt ein. Das von Ihnen beschriebene ist für mich neu. Ich muss jetzt über Vor- und Nachteile nachdenken. Eine inspirierende Frage mit aufschlussreichen Antworten ... (ich meine die Stream-Copy-Variante)
Wolf
1
Sie können einen Stream nicht kopieren, da das Kopieren von Puffern oft keinen Sinn ergibt (z. B. stdout). Sie können jedoch mehrere Stream-Objekte für denselben Puffer haben, wie in dieser Antwort vorgeschlagen. Während ein std:stringstreamWille sein eigenes unabhängiges std:stringbuf(ein std::streambufDerivat) schafft , das dann instd::cout.rdbuf()
n.caillou
Danke für die Klarstellung.
Wolf
0

Ich möchte die Antwort von qbert220 etwas verallgemeinern:

#include <ios>

class IoStreamFlagsRestorer
{
public:
    IoStreamFlagsRestorer(std::ios_base & ioStream)
        : ioStream_(ioStream)
        , flags_(ioStream_.flags())
    {
    }

    ~IoStreamFlagsRestorer()
    {
        ioStream_.flags(flags_);
    }

private:
    std::ios_base & ioStream_;
    std::ios_base::fmtflags const flags_;
};

Dies sollte auch für Eingabestreams und andere funktionieren.

PS: Ich hätte dies gerne einfach als Kommentar zur obigen Antwort gemacht. Der Stackoverflow erlaubt mir dies jedoch nicht, da der Ruf fehlt. Lassen Sie mich daher die Antworten hier anstelle eines einfachen Kommentars durcheinander bringen ...

J. Wilde
quelle