Эффективное использование библиотеки C++ iomanip
Я создал Vector
класс на C++ , и он отлично подходит для моих проблем. Теперь я очищаю его, и я столкнулся со следующим фрагментом кода:
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._y<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._z<<"]";
return output;
}
код позволяет печатать вектор как std::cout<<v<<std::endl;
. Каждое число имеет 23 пробела, из которых 16-десятичные. Текст выравнивается по правому краю, так что он будет печатать:
1.123456123456e+01
-1.123456123456e+01
вместо
1.123456123456e+01
-1.123456123456e+01
код кажется ужасно однообразным. Как вы можете "хранить" формат (все setiosflags
, setw
и setprecision
заявления), что вы можете сказать что-то вроде "печатать символы стандартным способом, но цифры с этим данный формат".
спасибо!
редактировать
согласно комментарию Роба Адамса, я изменил свой уродливый код (который, как указывали другие, испортил бы точность для "следующего парня") на более лаконичный (и правильный):
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)<<v._x<<", "
<<std::setw(23)<<v._y<<", "
<<std::setw(23)<<v._z
<<"]";
output.flags(f);
output.precision(p);
return output;
}
3 ответов
только std::setw()
- это временно. Два других звонка:--4--> и setprecision
иметь постоянный эффект.
таким образом, вы можете изменить свой код на:
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
return output;
}
но теперь вы родили флаги и точность для следующего парня. Попробуйте вместо этого:
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}
наконец, если вам абсолютно необходимо избавиться от дублирования константы 23
, вы можете сделать что-то вроде этого (но я бы не рекомендовал):
struct width {
int w;
width(int w) : w(w) {}
friend std::ostream& operator<<(std::ostream&os, const width& w) {
return os << std::setw(width.w);
}
};
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
width w(23);
output<<"["
<<w
<<v._x<<", "
<<w
<<v._y<<", "
<<w
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}
см. также этот другой вопрос, где они решили, что вы не можете сделать ширину постоянной.
все, кроме setw (), на самом деле это уже делают. Они "липкие".
ваша реальная проблема заключается в том, что происходит со следующим выходом после этого...
как правило, вы не используете стандартные манипуляторы напрямую. В в этом случае, например, можно определить манипулятора fromVector, и используйте это:
output << '['
<< fromVector << v.x << ", "
<< fromVector << v.y << ", "
<< fromVector << v.z << ']';
таким образом, если вы хотите изменить ширину и точность элементы в векторе, вам нужно сделать это только в одном месте.
в этом случае, когда у манипулятора нет аргументов, все это необходима простая функция:
std::ostream& fromVector(std::ostream& stream)
{
stream.setf(std::ios::right, std::ios::adjustfield);
stream.setf(std::ios::scientific, std::ios::floatfield);
stream.precision(16);
stream.width(32);
return stream;
}
конечно, это изменит большую часть форматирования для любого позже пользователь. В целом, было бы лучше использовать временный объект класса, который сохраняет состояние, и восстанавливает его в деструкторе. Я обычно получаю свои манипуляторы от что-то вроде:
заголовок: класс StateSavingManip { общественный: StateSavingManip( StateSavingManip const & other);
virtual ~StateSavingManip() ;
void operator()( std::ios& stream ) const ;
protected:
StateSavingManip() ;
private:
virtual void setState( std::ios& stream ) const = 0 ;
private:
StateSavingManip& operator=( StateSavingManip const& ) ;
private:
mutable std::ios* myStream ;
mutable std::ios::fmtflags
mySavedFlags ;
mutable int mySavedPrec ;
mutable char mySavedFill ;
} ;
inline std::ostream&
operator<<(
std::ostream& out,
StateSavingManip const&
manip )
{
manip( out ) ;
return out ;
}
inline std::istream&
operator>>(
std::istream& in,
StateSavingManip const&
manip )
{
manip( in ) ;
return in ;
}
источник: int getXAlloc() ; int ourXAlloc = getXAlloc () + 1;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1 ;
assert( ourXAlloc != 0 ) ;
}
return ourXAlloc - 1 ;
}
}
StateSavingManip::StateSavingManip()
: myStream( NULL )
{
}
StateSavingManip::StateSavingManip(
StateSavingManip const&
other )
{
assert( other.myStream == NULL ) ;
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != NULL ) {
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 == NULL ) {
backptr = const_cast< StateSavingManip* >( this ) ;
myStream = &stream ;
mySavedFlags = stream.flags() ;
mySavedPrec = stream.precision() ;
mySavedFill = stream.fill() ;
}
setState( stream ) ;
}
если вы это сделаете, вам придется добавить скобки после манипулятор, например:
output << '['
<< fromVector() << v.x << ", "
<< fromVector() << v.y << ", "
<< fromVector() << v.z << ']';
(Я уверен, что какая-нибудь умная душа придумает способ избегая их, но они никогда не беспокоили меня, так что я не обеспокоенный.)