Восстановить состояние std:: cout после его манипулирования

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

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..)?

Спасибо.

Ответ 1

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

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

//Your code here...

cout.flags( f );

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

Ответ 2

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

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

void printHex(std::ostream& x) {
    boost::io::ios_flags_saver ifs(x);
    x << std::hex << 123;
}

Ответ 3

Обратите внимание, что представленные здесь ответы не восстанавливают полное состояние 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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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