2009-01-01 4 views
28

Czy istnieje sposób na stałe ustawić manipulator std::setw (lub jego funkcję)? Spójrz na to:"Permanentny" std :: setw

#include <iostream> 
#include <iomanip> 
#include <algorithm> 
#include <iterator> 

int main(void) 
{ 
    int array[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 }; 
    std::cout.fill('0'); 
    std::cout.flags(std::ios::hex); 
    std::cout.width(3); 

    std::copy(&array[0], &array[9], std::ostream_iterator<int>(std::cout, " ")); 

    std::cout << std::endl; 

    for(int i = 0; i < 9; i++) 
    { 
    std::cout.width(3); 
    std::cout << array[i] << " "; 
    } 
    std::cout << std::endl; 
} 

Po biegu, widzę:

001 2 4 8 10 20 40 80 100 

001 002 004 008 010 020 040 080 100 

Tj każdy manipulator ma swoje miejsce oprócz tego, które musi być ustawione dla każdego wpisu. Czy istnieje jakiś elegancki sposób korzystania z std::copy (lub czegoś innego) wraz z setw? A z elegancji na pewno nie mam na myśli tworzenia własnego funktora ani funkcji do pisania rzeczy na std::cout.

Odpowiedz

16

Cóż, to niemożliwe. Nie ma sposobu, aby zadzwonić .width za każdym razem. Ale można użyć impuls, oczywiście:

#include <boost/function_output_iterator.hpp> 
#include <boost/lambda/lambda.hpp> 
#include <algorithm> 
#include <iostream> 
#include <iomanip> 

int main() { 
    using namespace boost::lambda; 
    int a[] = { 1, 2, 3, 4 }; 
    std::copy(a, a + 4, 
     boost::make_function_output_iterator( 
       var(std::cout) << std::setw(3) << _1) 
     ); 
} 

To ma stworzyć własną funktor, ale to się dzieje za sceną :)

7

Od setw i width nie powodują trwałego ustawienia jednego rozwiązaniem jest zdefiniowanie typu, który zastępuje wartość operator<<, stosując wartość domyślną: . Umożliwiłoby to użycie ostream_iterator dla tego typu z std::copy jak poniżej.

int fieldWidth = 4; 
std::copy(v.begin(), v.end(), 
    std::ostream_iterator< FixedWidthVal<int,fieldWidth> >(std::cout, ",")); 

Można definiować: (1) FixedWidthVal jako klasy szablonu z parametrami typu danych (typename) i szerokości (wartości), oraz (2) operator<< dla ostream i FixedWidthVal że stosuje setwdla każde wstawienie.

// FixedWidthVal.hpp 
#include <iomanip> 

template <typename T, int W> 
struct FixedWidthVal 
{ 
    FixedWidthVal(T v_) : v(v_) {} 
    T v; 
}; 

template <typename T, int W> 
std::ostream& operator<< (std::ostream& ostr, const FixedWidthVal<T,W> &fwv) 
{ 
    return ostr << std::setw(W) << fwv.v; 
} 

Wtedy można zastosować z std::copy (lub for pętla)

// fixedWidthTest.cpp 
#include <iostream> 
#include <algorithm> 
#include <iterator> 
#include "FixedWidthVal.hpp" 

int main() { 
    // output array of values 
    int array[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256 }; 

    std::copy(array,array+sizeof(array)/sizeof(int), 
     std::ostream_iterator< FixedWidthVal<int,4> >(std::cout, ",")); 

    std::cout << std::endl; 

    // output values computed in loop 
    std::ostream_iterator<FixedWidthVal<int, 4> > osi(std::cout, ","); 
    for (int i=1; i<4097; i*=2) 
     osi = i; // * and ++ not necessary 

    std::cout << std::endl; 

    return 0; 
} 

Output (demo)

1, 2, 4, 8, 16, 32, 64, 128, 256, 
    1, 2, 4, 8, 16, 32, 64, 128, 256, 512,1024,2048,4096, 
+1

Naprawdę ładny design, który myślę, że byłoby zastosowanie w wielu sytuacjach. Byłoby idealnie, gdyby szerokość była parametrem runtime (zamiast kompilacji), choć nie mogę wymyślić ładnego sposobu na uzyskanie tej informacji "w" 'ostream_iterator'. Można również podać funkcję wygody 'szablon with_width (T v) {return FixedWidthVal (v, szerokość); } ', aby zapisać, aby określić typ. –

+1

@j_random_hacker Cóż, powinienem dać kredyt tam, gdzie należy się kredyt. Przyjąłem to podejście z [pytanie codereview] (http://codereview.stackexchange.com/q/18291/35254), jedynie dodając parametr szablonu typu danych. Ładna sugestia dla funkcji wygody. – chappjc