Ответ 1
"Форсировать хранилища состояний IO" построены именно для этой цели.
Предположим, что у меня есть функция, которая принимает параметр ostream &
o
и записывает этот поток. Реализация operator <<
будет хорошим примером.
ostream& operator << (ostream& o, const MyThing& t)
{
// ... interesting code here ...
return o;
}
Внутри функции я могу указать параметры форматирования в потоке. Например, мне может потребоваться, чтобы число было напечатано как hex, независимо от того, как o
настроен, когда он передается функции.
Во-вторых, я могу захотеть сделать предположения о текущих флагах форматирования. Например, было бы неплохо иметь возможность предположить, что числа были отформатированы как десятичные, если я не попрошу иначе.
Наконец, к моменту выхода функции я хочу, чтобы параметры форматирования на o
были такими же, как они были до того, как была вызвана функция, чтобы они не изменились для вызывающего. Это просто вопрос вежливости вызывающему.
До сих пор я достиг этого, создав локальную ostringstream
внутри функции, выполнив всю свою работу над этим (включая параметры форматирования параметров) и отправив .str()
в o
в конце функции, Вопрос StackOverflow здесь говорит о том, что люди умнее меня используют один и тот же подход.. Однако мне мешает, что я храню столько данных в ostringstreams, которые, возможно, могут быть отправлены на выход раньше (строки могут стать довольно большими).
У меня есть два вопроса:
1) Является ли это законной, идиоматической, хорошей формой и т.д. создавать временный (основанный на стеке) поток вокруг o.rdbuf()
и выполнять мою работу над этим ostream? Мои собственные тесты и страница cppreference.com показывают, что я могу.
ostream& operator << (ostream& o_, const MyThing& t)
{
ostream o (o_.rdbuf());
// write stuff to "o",
// setting formatting options as I go.
return o_; // Formatting on the parameter ostream o_ unchanged.
}
2) Есть ли другой, лучший способ, который я не рассматривал?
"Форсировать хранилища состояний IO" построены именно для этой цели.
Это не плохое решение; это, безусловно, законно. Я не думаю он слишком распространен, поэтому, вероятно, неплохо прокомментировать почему вы это делаете.
Наиболее частым решением, которое я видел здесь, является создание состояния
класс сохранения, который сохранит все необходимое вам состояние
(обычно flags()
, precision()
и fill()
) в
конструктор и восстановить его в деструкторе, а затем
принудительно установите все нужные параметры. (Возможно, это возможно
использовать copyfmt
для этого, хотя это также копирует такие вещи, как
маска исключений, с которой вы, вероятно, не хотите играть.)
Настройки могут быть сохранены в виде объекта, называемого объектом fmtflags, определенного в классе с именем ios, который включен в iostream. Вы можете объявить один из эти объекты, но вы должны объявить его с помощью оператора разрешения области.
Следующий оператор сохранит определенные аспекты состояния формата в переменной old_settings:
ios::fmtflags old_settings = cout.flags();
Затем, после выполнения вывода с использованием нового параметра, вы можете восстановить старый параметр, вызвав ту же функцию со старыми настройками в качестве аргумента:
cout.flags(old_settings);
Другие настройки могут быть получены и восстановлены с помощью функций-членов. Например,
int old_precision = cout.precision();
сохранит текущую спецификацию точности. Тогда
cout.precision(old_precision);
вернет точность к исходному значению