2009-01-01 20 views
28

std::setw manipülatörünün (veya işlevinin width) nasıl kalıcı olarak ayarlanabileceği herhangi bir yol var mı? Şuna bak: koştuktan sonra"Kalıcı" 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; 
} 

, görüyorum:

001 2 4 8 10 20 40 80 100 

001 002 004 008 010 020 040 080 100 

yani Her manipülatör, her giriş için ayarlanması gereken setw/width dışında yerini tutar. std::copy'un (veya başka bir şeyin) setw ile birlikte nasıl kullanıldığını gösteren herhangi bir zarif yöntem var mı? Ve zarif olarak, kesinlikle kendi functor veya std::cout içine yazı yazmak için fonksiyon yaratmayı kastetmiyorum.

cevap

16

Bu mümkün değil. Her seferinde tekrar .width numaralı telefonu aramanın bir yolu yok.

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

O kendi functor oluşturmak, ancak kalıcı bir ortamda sonuçlanmayan sahnesi :)

7
setw yana

ve width arkasında olur mu, tek: Ama tabii ki, boost kullanabilirsiniz çözüm, setw değerini geçersiz kılan bir tür tanımlamaktır, bu değerden önce setw uygulanır. Bu, bu tür bir ostream_iterator'un aşağıdaki gibi std::copy ile çalışmasına izin verir.

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

tanımlayabilir olabilir: (1) bir veri türü parametreleri (typename) ve genişliği (değer) olan bir şablon sınıf olarak FixedWidthVal, ve (2) bir ostream için operator<< ve için setwgeçerli olan bir FixedWidthVal her bir ekleme.

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

Sonra std::copy uygulamasına imkan verir (ya da for döngü):

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

Çıktı (demo)

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

Gerçekten güzel bir tasarım bence uygulanabilir olacağını birçok durum için. Eğer genişlik bir çalışma zamanı (derleme zamanı yerine) olabilirse de bu ideal olurdu, ancak bu bilgiyi “ostream_iterator” içine almak için güzel bir yol düşünemiyorum. Ayrıca bir kolaylık fonksiyonu sağlayabilirsiniz 'template with_width (T v) {return FixedWidthVal (v, genişlik); türünü belirtmek zorunda olmak. –

+1

@j_random_hacker Eh, kredi vadesi nerede kredi vermeliyim. Bu yaklaşımı [codereview sorusu] 'dan (http://codereview.stackexchange.com/q/18291/35254), sadece veri türü şablon parametresini ekleyerek kabul ettim. Kolaylık işlevi için güzel öneri. – chappjc