2011-03-16 14 views
7

Creé una clase Vector en C++ y funciona muy bien para mis problemas. Ahora estoy limpiando para arriba, y me encontré con el siguiente fragmento de código:Uso efectivo de la biblioteca iomanip de 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; 
} 

El código permite imprimir un vector como std::cout<<v<<std::endl;. Cada número tiene 23 espacios, de los cuales 16 son los decimales. El texto está alineado a la derecha para que se imprimirá:

1.123456123456e+01 
-1.123456123456e+01 

En lugar de

1.123456123456e+01 
-1.123456123456e+01 

El código parece muy repetitivo. ¿Cómo se puede "almacenar" el formato (todas las declaraciones , setw y setprecision) para que pueda decir algo así como "imprimir los caracteres de forma estándar, pero los números con este formato dado".

¡Gracias!

Edición

Según comentario Rob Adams, he cambiado mi código feo (que, como se ha señalado por otros, lo arruinaba la precisión para el 'tipo de al lado') a una más breve (y correcto):

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

No es exactamente un duplicado de http://stackoverflow.com/questions/405039/permanent-stdsetw –

Respuesta

10

Sólo std::setw() es temporal. Las otras dos llamadas, y setprecision tienen un efecto permanente.

Por lo tanto, se puede cambiar el código para:

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

Pero ahora que ha borked las banderas y precisión para el tipo de al lado. Tal vez puedas probar:

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

Por último, si es absolutamente necesario para deshacerse de la duplicación de la constante 23, se podría hacer algo como esto (pero yo no lo recomendaría):

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

Vea también this other question, donde decidieron que no puede hacer que el ancho sea permanente.

+0

Muchas gracias, no solo lo hiciste resolver mi problema, pero también ta Me dio algo sobre el concepto de flujo general. ¡Gracias! – Escualo

1

Todo menos el setw() en realidad ya lo hace. Ellos son "pegajosos".

Su problema real es lo que pasa a la siguiente salida después de que éste ...

1

Generalmente, no utiliza los manipuladores estándar directamente. En este caso, por ejemplo, podría definir un manipulador fromVector, y el uso que:

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

esta manera, si desea cambiar el ancho y la precisión de elementos en el vector, es suficiente con hazlo en un solo lugar

En este caso, en que el manipulador no tiene argumentos, todo lo que se necesita es una función simple:

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

Por supuesto, esto habrá cambiado mayor parte del formato para cualquier usuario tarde. En general, sería una buena práctica usar un objeto de clase temporal que guarda el estado y lo restaura en el destructor. Por lo general derivo mis manipuladores de algo como:

cabecera: clase StateSavingManip { pública: StateSavingManip ( StateSavingManip const & otra);

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

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

Si lo hace, tendrá que añadir entre paréntesis después del manipulador, por ejemplo:

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

(estoy seguro de que algún alma inteligente va a encontrar una manera de evitar ellos, pero nunca me han molestado, así que no me molestó .)

Cuestiones relacionadas