Подтвердить что ты не робот

"Откат" или "Отменить" любые манипуляторы, применяемые к потоку, не зная, что такое манипуляторы

Если я применяю произвольное количество манипуляторов к потоку, есть ли способ отменить применение этих манипуляторов общим способом?

Например, рассмотрим следующее:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    cout << "Hello" << hex << 42 << "\n";
    // now i want to "roll-back" cout to whatever state it was in
    // before the code above, *without* having to know 
    // what modifiers I added to it

    // ... MAGIC HAPPENS! ...

    cout << "This should not be in hex: " << 42 << "\n";
}

Предположим, я хочу добавить код в MAGIC HAPPENS, который вернет состояние манипуляторов потока до того, что было до того, как я сделал cout << hex. Но я не знаю, какие манипуляторы я добавил. Как я могу это сделать?

Другими словами, я хотел бы написать что-то вроде этого (psudocode/fantasy code):

std::something old_state = cout.current_manip_state();
cout << hex;
cout.restore_manip_state(old_state);

Возможно ли это?

EDIT:

Если вам интересно, я заинтересован в том, чтобы сделать это в пользовательском operator<<(), который я пишу для сложного типа. Тип представляет собой своего рода дискриминационный союз, а разные типы значений будут иметь разные манипуляции, применяемые к потоку.

EDIT2:

Ограничение: я не могу использовать Boost или любые другие сторонние библиотеки. Решение должно быть в стандартном С++.

4b9b3361

Ответ 1

Да.

Вы можете сохранить состояние и восстановить его:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{

    std::ios  state(NULL);
    state.copyfmt(std::cout);

    cout << "Hello" << hex << 42 << "\n";
    // now i want to "roll-back" cout to whatever state it was in
    // before the code above, *without* having to know what modifiers I added to it

  // ... MAGIC HAPPENS! ...

    std::cout.copyfmt(state);
    cout << "This should not be in hex: " << 42 << "\n";
}

Если вы хотите вернуться к состоянию по умолчанию, вам даже не нужно сохранять состояние, которое вы можете извлечь из временного объекта.

std::cout.copyfmt(std::ios(NULL));

Ответ 2

Стандартные манипуляторы управляют флагами формата потока, настройками точности и ширины. Значение ширины reset для большинства форматированных операций вывода в любом случае. Все они могут быть восстановлены следующим образом:

std::ios_base::fmtflags saveflags = std::cout.flags();
std::streamsize prec = std::cout.precision();
std::streamsize width = std::cout.width();

и восстановлен:

std::cout.flags( saveflags );
std::cout.precision( prec );
std::cout.width( width );

Превращение этого в класс RAII - упражнение для читателя...

Ответ 4

Сохранение и восстановление состояния не является безопасным для исключений. Я бы предложил перетасовать все в stringstream, и, наконец, вы положили это на реальный поток (который никогда не менял свои флаги вообще).

#include <iostream>
#include <iomanip>
#include <sstream>

int main()
{
    std::ostringstream out;
    out << "Hello" << std::hex << 42 << "\n";
    std::cout << out.str();

    // no magic necessary!

    std::cout << "This should not be in hex: " << 42 << "\n";
}

Конечно, это немного менее результативно. Идеальные решения зависят от ваших конкретных потребностей.

Ответ 5

Я знаю, что это старый вопрос, но для будущих поколений:

Вы также можете написать простой хранитель состояния самостоятельно (это, безусловно, поможет вам избежать изменения состояния). Просто используйте решение, предложенное @loki, и запустите его из конструктора/деструктора объекта (короче: RAII) в следующих строках:

class stateSaver
{
  public:
    stateSaver(ostream& os): stream_(os), state_(nullptr) { state_.copyfmt(os); }
    ~stateSaver() { stream_.copyfmt(state_); }
  private:
    std::ios state_;
    ostream& stream_;
};

Затем вы будете использовать его следующим образом:

void myFunc() {
    stateSaver state(cout);
    cout << hex << 42 << endl; // will be in hex
}

int main() {
    cout << 42 << endl; // will be in dec
    myFunc();
    cout << 42 << endl; // will also be in dec
}