std::num_pu...put::do_put(3)
πΈ NAME
std::num_put::put, std::num_put::do_put β bestie, this is the section 3 C++ Standard Library manual page for the number-formatting facet functions. The manual page name is std::num_put::put,std::num_put::do_put π
(the header was being extra and truncating itself into std::num_pu...put::do_put(3), iconic).
π Synopsis
Defined in header <locale> β¨
public:
iter_type put( iter_type out, std::ios_base& str,
char_type fill, bool val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, long val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, long long val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, unsigned long val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, unsigned long long val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, double val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, long double val ) const;
iter_type put( iter_type out, std::ios_base& str,
char_type fill, const void* val ) const;
protected:
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, bool val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, long val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, long long val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, unsigned long val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, unsigned long long val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, double val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, long double val ) const;
virtual iter_type do_put( iter_type out, std::ios_base& str,
char_type fill, const void* val ) const;
- The
long longandunsigned long longoverloads of bothputanddo_putare since C++11 π 1)The public member function calls the protected virtual member functiondo_putof the most derived class.2)Writes characters to the output sequenceoutwhich represent the value ofval, formatted as requested by the formatting flagsstr.flags()and thestd::numpunctandstd::ctypefacets of the locale imbued in the streamstr. This function is called by all formatted output stream operators, such asstd::cout << n;.
Conversion occurs in four stages:
Stage 1: conversion specifier selection
First, the I/O format flags are obtained, as if by:
fmtflags basefield = (str.flags() & std::ios_base::basefield);
fmtflags uppercase = (str.flags() & std::ios_base::uppercase);
fmtflags floatfield = (str.flags() & std::ios_base::floatfield);
fmtflags showpos = (str.flags() & std::ios_base::showpos);
fmtflags showbase = (str.flags() & std::ios_base::showbase);
fmtflags showpoint = (str.flags() & std::ios_base::showpoint);
- If the type of
valisbool:- If
boolalpha == 0, then it convertsvalto typeintand performs integer output. - If
boolalpha != 0, it obtainsstd::use_facet<std::numpunct<CharT>>(str.getloc()).truename()ifval == trueorstd::use_facet<std::numpunct<CharT>>(str.getloc()).falsename()ifval == false, and outputs each successive charactercof that string tooutwith*out++ = c. No further processing is done in this case; the function returnsout.
- If
- If the type of
valis an integer type, the first applicable choice of the following is selected:- If
basefield == oct, it will use conversion specifier%o - If
basefield == hex && !uppercase, it will use conversion specifier%x - If
basefield == hex, it will use conversion specifier%X - If the type of
valis signed, it will use conversion specifier%d - If the type of
valis unsigned, it will use conversion specifier%u
- If
- For integer types, a length modifier is added to the conversion specification if necessary:
lforlongandunsigned long,llforlong longandunsigned long long(since C++11). - If the type of
valis a floating-point type, the first applicable choice of the following is selected:- If
floatfield == std::ios_base::fixed, it will use conversion specifier%f - If
floatfield == std::ios_base::scientific && !uppercase, it will use conversion specifier%e - If
floatfield == std::ios_base::scientific, it will use conversion specifier%E - If
floatfield == (std::ios_base::fixed | std::ios_base::scientific) && !uppercase, it will use conversion specifier%a(since C++11) - If
floatfield == (std::ios_base::fixed | std::ios_base::scientific), it will use conversion specifier%A - If
!uppercase, it will use conversion specifier%g - Otherwise, it will use conversion specifier
%G
- If
Also:
- If the type of
valislong double, the length modifierLis added to the conversion specifier. - If the type of
valis a floating-point type andfloatfield != (ios_base::fixed | ios_base::scientific)(since C++11), the precision modifier is added and set tostr.precision(). Otherwise, no precision is specified. - For both integer and floating-point types, if
showposis set, the modifier+is prepended. - For integer types, if
showbaseis set, the modifier#is prepended. - For floating-point types, if
showpointis set, the modifier#is prepended. - If the type of
valisvoid*, it will use conversion specifier%p. - A narrow character string is created as if by a call to
std::printf(spec, val)in the"C"locale, wherespecis the chosen conversion specifier.
Stage 2: locale-specific conversion
- Every character
cobtained in Stage 1, other than the decimal point'.', is converted toCharTby callingstd::use_facet<std::ctype<CharT>>(str.getloc()).widen(c). - For arithmetic types, the thousands separator character, obtained from
std::use_facet<std::numpunct<CharT>>(str.getloc()).thousands_sep(), is inserted into the sequence according to the grouping rules provided bystd::use_facet<std::numpunct<CharT>>(str.getloc()).grouping() - Decimal point characters (
'.') are replaced bystd::use_facet<std::numpunct<CharT>>(str.getloc()).decimal_point()
Stage 3: padding
- The adjustment flag is obtained as if by
std::fmtflags adjustfield = (flags & (std::ios_base::adjustfield))and examined to identify padding location, as follows:- If
adjustfield == std::ios_base::left, it will pad after - If
adjustfield == std::ios_base::right, it will pad before - If
adjustfield == std::ios_base::internaland a sign character occurs in the representation, it will pad after the sign - If
adjustfield == std::ios_base::internaland the Stage 1 representation began with0xor0X, it will pad after thexorX - Otherwise, it will pad before
- If
- If
str.width()is non-zero (e.g.std::setwwas just used) and the number ofCharT's after Stage 2 is less thanstr.width(), then copies of the fill character are inserted at the position indicated by padding to bring the length of the sequence tostr.width().
In any case, str.width(0) is called to cancel the effects of std::setw.
Stage 4: output
Every successive character c from the sequence of CharT's from Stage 3 is output as if by *out++ = c.
π«Ά Parameters
outβ iterator pointing to the first character to be overwrittenstrβ stream to retrieve the formatting information fromfillβ padding character used when the results needs to be padded to the field widthvalβ value to convert to string and output
π Return value
out
π§ Notes
The leading zero generated by the conversion specification #o (resulting from the combination of std::showbase and std::oct for example) is not counted as a padding character.
When formatting a floating point value as hexfloat (i.e., when floatfield == (std::ios_base::fixed | std::ios_base::scientific)), the (since C++11) stream's precision is not used; instead, the number is always printed with enough precision to exactly represent the value.
π Example
Output a number using the facet directly, and demonstrate user-defined facet:
// Run this code
#include <iostream>
#include <locale>
// this custom num_put outputs squares of all integers (except long long)
struct squaring_num_put : std::num_put<char>
{
iter_type do_put(iter_type out, std::ios_base& str,
char_type fill, long val) const
{
return std::num_put<char>::do_put(out, str, fill, val * val);
}
iter_type do_put(iter_type out, std::ios_base& str,
char_type fill, unsigned long val) const
{
return std::num_put<char>::do_put(out, str, fill, val * val);
}
};
int main()
{
auto& facet = std::use_facet<std::num_put<char>>(std::locale());
facet.put(std::cout, std::cout, '0', 2.71);
std::cout << '\n';
std::cout.imbue(std::locale(std::cout.getloc(), new
squaring_num_put));
std::cout << 6 << ' ' << -12 << '\n';
}
...and that's literally the output, bestie β here's what you get when you run it:
2.71
36 144
Two examples, two outputs, zero surprises. β¨
Now πΈ the part you'll actually use in real life πΈ β an implementation of operator<< for a user-defined type. This is the whole "make my custom type print nicely by hand-forging it through num_put" move, and it's giving main character energy ngl:
#include <iostream>
#include <iterator>
#include <locale>
struct base { long x = 10; };
template<class CharT, class Traits>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, base const& b)
{
try
{
typename std::basic_ostream<CharT, Traits>::sentry s(os);
if (s)
{
std::ostreambuf_iterator<CharT, Traits> it(os);
std::use_facet<std::num_put<CharT>>(os.getloc())
.put(it, os, os.fill(), b.x);
}
}
catch (...)
{
// set badbit on os and rethrow if required
}
return os;
}
int main()
{
base b;
std::cout << b;
}
That // Run this code line you might see floating around next to it is just the cppreference clicky-button, iykyk β the code above is the whole thing, go paste it somewhere and vibe. π
Output:
10
Ten. That's it. That's the whole personality. π
π Defect reports π
Okay so this is the spicy part β the following behavior-changing defect reports were applied retroactively to previously published C++ standards. Which means past-you could have been living a lie. π
LWG 34β applied to C++98: as published, thebooloverload used non-existing memberstruenameandfalsenameofstd::ctype; the correct behavior is to use these members ofstd::numpunct. (they literally invented members that did not exist. iconic audacity.)LWG 231β applied to C++98: as published, the precision modifier was only added if(flags & fixed) != 0orstr.precision() > 0; those conditions were removed.LWG 282β applied to C++98: as published, the thousand separators were only inserted for integral types in stage 2; the correct behavior is that they're also inserted for floating-point types.
Note bestie: these are retroactive β the fix applies backwards to the standard as published, so don't go assuming old compilers behave the old way without checking fr fr.
π See also π
operator<<β inserts formatted data (public member function ofstd::basic_ostream<CharT,Traits>) π
And that's a wrap on do_put, bestie β you now know more about locale-aware number formatting than 99% of the group chat. Go forth and pad correctly. π«Ά
Source: cppreference.com Β· Version: 2024.06.10 Β· slay, and remember: every flag is a choice, make them wisely π
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms