2012-11-11 16 views
12

Come implementare il mio manipolatore flusso personalizzato in modo che sia appiccicoso. Per esempio, io voglio convertire interi in binario in modo tale che:Manipolatore personalizzato flusso personalizzato

cout << "decimal of 4: " << 4 
    << "\ndecimal of 4: " << 4 
    << binary << "\nbinary of 4: " << 4 
    << "\nbinary of 4: " << 4 
    << nobinary << "\ndecimal of 4: " << 4 
    << "\ndecimal of 4: " << 4 << endl; 

sarebbero tornati:

decimal of 4: 4 
decimal of 4: 4 
binary of 4: 100 
binary of 4: 100 
decimal of 4: 4 
decimal of 4: 4 
+3

Io ucciderei la nozione 'nobinary' e lasciare che usassero 'std :: dec' ecc. – chris

+1

Vuoi che rimanga fermo anche dopo la fine dell'istruzione? –

+2

BTW, [non usare 'std :: endl'] (http://kuhllib.com/2012/01/14/stop-excessive-use-of-stdendl/). –

risposta

16

Fare le cose insieme è un po 'coinvolti. Per renderlo comprensibile, inizierò con le cose di base: utilizzo di flag di formattazione personalizzati per i tipi definiti dall'utente. La formattazione personalizzata degli interi seguirà di seguito.

classi

L'iostream derivano [indirettamente] dal std::ios_base che fornisce due negozi per i dati: std::ios_base::iword() e std::ios_base::pword() per int s e void*, rispettivamente. Il mantenimento della memoria allocata memorizzata con std::ios_base::pword() non è banale e, fortunatamente, non è necessario per questo caso d'uso relativamente semplice. Per utilizzare queste funzioni che restituiscono un riferimento non-const al tipo corrispondente, normalmente si assegna un indice utilizzando std::ios_base::xalloc() una volta nel programma e lo si utilizza ogni volta che è necessario accedere ai flag di formattazione personalizzati. Quando si accede a un valore con o pword() inizialmente verrà inizializzato a zero. Per mettere le cose insieme, ecco un piccolo programma che dimostra questo:

#include <iostream> 

static int const index = std::ios_base::xalloc(); 

std::ostream& custom(std::ostream& stream) { 
    stream.iword(index) = 1; 
    return stream; 
} 

std::ostream& nocustom(std::ostream& stream) { 
    stream.iword(index) = 0; 
    return stream; 
} 

struct mytype {}; 
std::ostream& operator<< (std::ostream& out, mytype const&) { 
    return out << "custom-flag=" << out.iword(index); 
} 

int main() 
{ 
    std::cout << mytype() << '\n'; 
    std::cout << custom; 
    std::cout << mytype() << '\n'; 
    std::cout << nocustom; 
    std::cout << mytype() << '\n'; 
} 

Ora, un int come 4 non è un tipo di utente-definire e c'è già un operatore di output definito per questi. Fortunatamente, è possibile personalizzare il modo in cui gli interi vengono formattati utilizzando le faccette, in particolare usando std::num_put<char>. Ora, per farlo è necessario fare una serie di passaggi:

  1. derivare una classe std::num_put<char> e sovrascrivere il do_put() membri che si desidera dare il comportamento specializzato per.
  2. Creare un oggetto std::locale utilizzando il nuovo aspetto.
  3. std::ios_base::imbue() lo stream con il nuovo std::locale.

Per rendere le cose più bello per l'utente, si potrebbe desiderare di evocare una nuova std::locale con un adeguato std::num_put<char> aspetto quando si utilizza il manipolatore. Tuttavia, prima di farlo, cominciamo via con la creazione di un aspetto adatto:

#include <bitset> 
#include <iostream> 
#include <limits> 
#include <locale> 

static int const index = std::ios_base::xalloc(); 

class num_put 
    : public std::num_put<char> 
{ 
protected: 
    iter_type do_put(iter_type to, 
        std::ios_base& fmt, 
        char_type fill, 
        long v) const 
    { 
     if (!fmt.iword(index)) { 
      return std::num_put<char>::do_put(to, fmt, fill, v); 
     } 
     else { 
      std::bitset<std::numeric_limits<long>::digits> bits(v); 
      size_t i(bits.size()); 
      while (1u < i && !bits[i - 1]) { 
       --i; 
      } 
      for (; 0u < i; --i, ++to) { 
       *to = bits[i - 1]? '1': '0'; 
      } 
      return to; 
     } 
    } 
#if 0 
    // These might need to be added, too: 
    iter_type do_put(iter_type, std::ios_base&, char_type, 
        long long) const; 
    iter_type do_put(iter_type, std::ios_base&, char_type, 
        unsigned long) const; 
    iter_type do_put(iter_type, std::ios_base&, char_type, 
        unsigned long long) const; 
#endif 
}; 

std::ostream& custom(std::ostream& stream) { 
    stream.iword(index) = 1; 
    return stream; 
} 

std::ostream& nocustom(std::ostream& stream) { 
    stream.iword(index) = 0; 
    return stream; 
} 

int main() 
{ 
    std::locale loc(std::locale(), new num_put); 
    std::cout.imbue(loc); 
    std::cout << 13 << '\n'; 
    std::cout << custom; 
    std::cout << 13 << '\n'; 
    std::cout << nocustom; 
    std::cout << 13 << '\n'; 
} 

Che cosa è un po 'brutto è che necessario imbue() l'usanza std::locale di utilizzare il custom manipolatore. Per sbarazzarsi di questo, possiamo solo fare in modo l'aspetto personalizzato viene installato nella usato std::locale e, se non lo è, basta installarlo quando si imposta il flag:

std::ostream& custom(std::ostream& stream) { 
    if (!stream.iword(index) 
     && 0 == dynamic_cast<num_put const*>(
       &std::use_facet<std::num_put<char> >(stream.getloc()))) { 
     stream.imbue(std::locale(stream.getloc(), new num_put)); 
    } 
    stream.iword(index) = 1; 
    return stream; 
} 

Quello che oggi è rimasto è quello anche di override i diversi membri do_put() per funzionare correttamente con i vari tipi unsigned e con long long ma questo è lasciato come esercizio.

+0

La cosa che non ho avuto è stata la dynamic_cast. A cosa serve? – 0x499602D2

+0

@ 0x499602D2: Tutto il 'dynamic_cast' verifica se uno' std :: locale' personalizzato deve essere 'imbue()' ed: Se è già installato un facet adatto, non è necessario 'imbue()' un nuovo aspetto Osservando la modifica, sembra che la modifica precedente abbia inserito il codice nel codice precedentemente corretto! 'Dynamic_cast' ha provato a testare il facet personalizzato' num_put', non per 'std :: num_put '! –

+0

Mi dispiace per quello. Ho fatto una modifica sbagliata. lol – 0x499602D2

Problemi correlati