Восстановите состояние std :: cout после манипулирования им - PullRequest
87 голосов
/ 16 февраля 2010

Предположим, у меня есть такой код:

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

У меня вопрос: есть ли способ «восстановить» состояние cout до его исходного состояния после возвращения из функции? (Что-то вроде std :: boolalpha и std :: noboolalpha ..)?

Спасибо.

Ответы [ 6 ]

78 голосов
/ 16 февраля 2010

вам нужно #include <iostream> или #include <ios>, тогда при необходимости:

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

//Your code here...

cout.flags( f );

Вы можете поместить их в начало и конец своей функции или проверить этот ответ о том, как использовать это с RAII .

55 голосов
/ 16 февраля 2010

Boost IO Stream State Saver кажется именно тем, что вам нужно. : -)

Пример на основе вашего фрагмента кода:

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}
37 голосов
/ 21 июня 2015

Обратите внимание, что ответы, представленные здесь, не восстановят полное состояние std::cout. Например, std::setfill будет «прилипать» даже после вызова .flags(). Лучшее решение - использовать .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;

Напечатает:

case closed

вместо:

case closed0000
19 голосов
/ 16 сентября 2013

Я создал класс RAII, используя пример кода из этого ответа. Большое преимущество этой техники имеет место, если у вас есть несколько путей возврата из функции, которая устанавливает флаги в iostream. Какой бы путь возврата не использовался, деструктор всегда будет вызываться, а флаги всегда будут сброшены. Нет возможности забыть восстановить флаги, когда функция вернется.

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

Затем вы будете использовать его, создавая локальный экземпляр IosFlagSaver всякий раз, когда захотите сохранить текущее состояние флага. Когда этот экземпляр выходит из области видимости, состояние флага будет восстановлено.

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;
}
7 голосов
/ 16 февраля 2010

С небольшими изменениями, чтобы сделать вывод более читабельным:

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..
}
4 голосов
/ 04 мая 2017

Вы можете создать еще одну оболочку вокруг буфера 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"
}

В функции:

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

Конечно, если производительность является проблемой, это немного дороже, потому что этокопирование всего объекта ios (но не буфера), включая некоторые вещи, за которые вы платите, но вряд ли будете использовать, например, локаль.

В противном случае я чувствую, что вы собираетесь использовать .flags() лучше быть последовательным и использовать .setf(), а не синтаксис << (чистый вопрос стиля).

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

Как уже говорили другие, вы можете поместить вышеприведенное (и .precision()и .fill(), но, как правило, не относящиеся к языку и словам, которые, как правило, не будут изменены и более тяжелые) в классе для удобства и для обеспечения его исключительной безопасности;конструктор должен принять std::ios&.

...