Обычное решение в C++ состоит в том, чтобы определить манипуляторы, которые указывают, что вы пытаетесь отформатировать, а не взламывать физические значения непосредственно в точке вывода. (Одним из возможных исключений является ширина, где std::setw
может быть полезен напрямую.) Таким образом, например, при фактическом выводе чего-либо вы не будете указывать нулевое заполнение или фиксированное с двумя десятичными знаками, а что-то вроде:
std::cout << temperature(2) << theTemporature;
где temperature
будет что-то вроде:
class temperature
{
int myMinWidth;
public:
temperature( int minWidth )
: myMinWidth( minWidth )
{
}
friend std::ostream& operator<<( std::ostream& dest, temperature const& manip )
{
dest.setf( std::ios_base::fixed, std::ios_base::floatfield );
dest.precision( 2 );
dest.width( myMinWidth );
return dest;
}
};
Список доступных модификаций формата см. в спецификации std::ios_base
и в полях std::ios_base::fmtflags
.
Если вы делаете много вывода, вы можете изменить это, чтобы восстановить исходные флаги формата в конце полного выражения. (Вся информация о формате, кроме ширины, является фиксированной, поэтому принудительное фиксированное форматирование здесь оставляет вам фиксированный формат для остальной части программы, что не обязательно является тем, что вам нужно.) Я использую следующий базовый класс для всех моих манипуляторы:
class StateSavingManip
{
public:
void operator()( std::ios& stream ) const;
protected:
StateSavingManip() : myStream( nullptr ) {}
~StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
реализация:
namespace {
int getXAlloc() ;
int ourXAlloc = getXAlloc() + 1 ;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1 ;
assert( ourXAlloc != 0 ) ;
}
return ourXAlloc - 1 ;
}
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != nullptr ) {
myStream->flags( mySavedFlags ) ;
myStream->precision( mySavedPrec ) ;
myStream->fill( mySavedFill ) ;
myStream->pword( getXAlloc() ) = NULL ;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() ) ;
if ( backptr == nullptr ) {
backptr = const_cast< StateSavingManip* >( this ) ;
myStream = &stream ;
mySavedFlags = stream.flags() ;
mySavedPrec = stream.precision() ;
mySavedFill = stream.fill() ;
}
setState( stream ) ;
}
Обратите внимание на использование поля pword
, чтобы гарантировать, что только первый временный манипулятор восстановит формат; деструкторы будут вызываться в порядке, обратном построению, но порядок построения обычно не указывается, если в выражении имеется более одного такого манипулятора.
Наконец: не все возможно с использованием этой техники: если вы хотите систематически добавлять знак градуса к температуре, это невозможно сделать. В этом случае вам нужно определить класс Temperature и перегрузить для него оператор <<
; это позволяет все, что только можно вообразить (намного больше, чем вы когда-либо могли бы достичь с форматированием в стиле printf
).
person
James Kanze
schedule
12.12.2013
<iomanip>
в C++. Возможно, это вряд ли полезно в курсе, но полезно знать об этом. - person chris   schedule 12.12.2013%
в качестве оператора вставки, а также спецификатора формата, что приводит к практически нечитаемому коду. (Python попробовал это и, наконец, переключился на использование функций-членовstring
,format
. Решение не работает так же хорошо в C++, потому что для него требуется средство безопасного типа varargs.) И, конечно, даже сboost::format
вы бы хотите использовать манипуляторы, чтобы информация о форматировании была отделена от текста (что важно для обслуживания). - person James Kanze   schedule 12.12.2013