Effective use of C++ iomanip library

感情迁移 提交于 2019-11-28 10:17:07
Robᵩ

Only std::setw() is temporary. The other two calls, setiosflags, and setprecision have a permanent effect.

So, you could change your code to :

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;
} 

But now you've borked the flags and precision for the next guy. Try this instead:

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;
} 

Finally, if you absolutely have to get rid of the duplication of the constant 23, you could do something like this (but I wouldn't recommend it):

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;
} 

See also this other question, where they decided that you can't make width permanent.

Everything but the setw() actually do that already. They are "sticky".

Your real problem is what happens to the next output after this one...

Generally, you don't use the standard manipulators directly. In this case, for example, you might define a manipulator fromVector, and use that:

output << '['
       << fromVector << v.x << ", "
       << fromVector << v.y << ", "
       << fromVector << v.z << ']';

That way, if you want to change the width and precision of elements in the vector, you only have to do it in one place.

In this case, where the manipulator has no arguments, all that's needed is a simple function:

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;
}

Of course, this will have changed most of the formatting for any later user. In general, it would be better practice to use a temporary class object which saves the state, and restores it in the destructor. I usually derive my manipulators from something like:

header: class StateSavingManip { public: 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 ;
}

source: 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 ) ;
}

If you do this, you'll have to add parentheses after the manipulator, e.g.:

output << '['
       << fromVector() << v.x << ", "
       << fromVector() << v.y << ", "
       << fromVector() << v.z << ']';

(I'm sure that some clever soul will figure out a way of avoiding them, but they've never bothered me, so I haven't bothered.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!