"Откат" или "Отменить" любые манипуляторы, применяемые к потоку, не зная, что такое манипуляторы
Если я применяю произвольное количество манипуляторов к потоку, есть ли способ отменить применение этих манипуляторов общим способом?
Например, рассмотрим следующее:
#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 или любые другие сторонние библиотеки. Решение должно быть в стандартном С++.
Ответы
Ответ 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 - упражнение для читателя...
Ответ 3
Ускорение заставки в режиме ввода-вывода может помочь.
http://www.boost.org/doc/libs/1_40_0/libs/io/doc/ios_state.html
Ответ 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
}