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:
- derivare una classe
std::num_put<char>
e sovrascrivere il do_put()
membri che si desidera dare il comportamento specializzato per.
- Creare un oggetto
std::locale
utilizzando il nuovo aspetto.
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.
Io ucciderei la nozione 'nobinary' e lasciare che usassero 'std :: dec' ecc. – chris
Vuoi che rimanga fermo anche dopo la fine dell'istruzione? –
BTW, [non usare 'std :: endl'] (http://kuhllib.com/2012/01/14/stop-excessive-use-of-stdendl/). –