La solution «évidente» consiste à utiliser un manipulateur pour installer une facette personnalisée std::num_put<char>
qui formate simplement int
s comme vous le souhaitez.
La déclaration ci-dessus peut être un peu énigmatique bien qu'elle décrive entièrement la solution. Voici le code pour implémenter la logique. Le premier ingrédient est un std::num_put<char>
facette qui est juste une classe dérivée de std::num_put<char>
et remplaçant une de ses virtual
fonctions. La facette utilisée est une facette de filtrage qui regarde un drapeau stocké avec le flux (en utilisant iword()
) pour déterminer s'il doit changer le comportement ou non. Voici le code:
class num_put
: public std::num_put<char>
{
std::locale loc_;
static int index() {
static int rc(std::ios_base::xalloc());
return rc;
}
friend std::ostream& twodigits(std::ostream&);
friend std::ostream& notwodigits(std::ostream&);
public:
num_put(std::locale loc): loc_(loc) {}
iter_type do_put(iter_type to, std::ios_base& fmt,
char fill, long value) const {
if (fmt.iword(index())) {
fmt.width(2);
return std::use_facet<std::num_put<char> >(this->loc_)
.put(to, fmt, '0', value);
}
else {
return std::use_facet<std::num_put<char> >(this->loc_)
.put(to, fmt, fill, value);
}
}
};
La partie principale est la fonction de membre do_put()
qui décide comment la valeur doit être formatée: Si le drapeau dans fmt.iword(index())
est non nul, il fixe la largeur de 2
et appelle la mise en forme fonctionne avec un caractère de remplissage de 0
. La largeur va être réinitialisée de toute façon et le caractère de remplissage n'est pas stocké avec le flux, c'est-à-dire qu'il n'y a aucun besoin de nettoyage.
Normalement, le code devrait probablement vivre dans une unité de traduction séparée et il ne serait pas déclaré dans un en-tête. Les seules fonctions réellement déclarées dans un en-tête sont twodigits()
et notwodigits()
qui sont faites friend
dans ce cas pour donner accès à la fonction membre index()
. La fonction membre index()
alloue juste un index utilisable avec std::ios_base::iword()
lorsqu'il est appelé l'heure et il retourne juste cet index.Les manipulateurs twodigits()
et notwodigits()
définissent principalement cet index. Si la num_put
facette est pas installé pour le flux twodigits()
installe également la facette:
std::ostream& twodigits(std::ostream& out)
{
if (!dynamic_cast<num_put const*>(
&std::use_facet<std::num_put<char> >(out.getloc()))) {
out.imbue(std::locale(out.getloc(), new num_put(out.getloc())));
}
out.iword(num_put::index()) = true;
return out;
}
std::ostream& notwodigits(std::ostream& out)
{
out.iword(num_put::index()) = false;
return out;
}
Le manipulateur twodigits()
alloue la num_put
facette en utilisant new num_put(out.getloc())
. Il ne nécessite aucun nettoyage car l'installation d'une facette dans un objet std::locale
effectue le nettoyage nécessaire. Le std::locale
d'origine du flux est accessible en utilisant out.getloc()
. Il est changé par la facette. En théorie, le notwodigits
peut restaurer l'original std::locale
au lieu d'utiliser un indicateur. Cependant, imbue()
peut être une opération relativement coûteuse et l'utilisation d'un drapeau devrait être beaucoup moins cher. Bien sûr, s'il y a beaucoup de drapeaux de mise en forme similaires, les choses peuvent devenir différentes ...
Pour démontrer l'utilisation des manipulateurs, il existe un programme de test simple ci-dessous. Il met en place le formatage drapeau twodigits
deux fois pour vérifier que la facette est créé uniquement une fois (ce serait un peu ridicule de créer une chaîne de std::locale
s pour passer à travers la mise en forme:
int main()
{
std::cout << "some-int='" << 1 << "' "
<< twodigits << '\n'
<< "two-digits1='" << 1 << "' "
<< "two-digits2='" << 2 << "' "
<< "two-digits3='" << 3 << "' "
<< notwodigits << '\n'
<< "some-int='" << 1 << "' "
<< twodigits << '\n'
<< "two-digits4='" << 4 << "' "
<< '\n';
}
Création d'une nouvelle 'ostream & operator <<' pour 'int' est une très mauvaise idée. :) –
@LightnessRacesinOrbit Oui, je l'ai compris aussi. Des suggestions quoi faire à la place? – Ali
si vous essayez de mettre en forme la date/l'heure, vous pouvez vouloir vérifier ceci: http://en.cppreference.com/w/cpp/io/manip/put_time – zahir