Document #: | D1636R0 |
Date: | 2019-04-19 |
Project: | Programming Language C++ Library Evolution |
Reply-to: |
Lars Gullik Bjønnes <[email protected]> |
After P0645 and P1361 almost all types that have a output stream operator<<
overload will also have a std::formatter
specialization. The following types, which have output stream operators, are missing this specialization:
basic_streambuf
bitset
complex
error_code
filesystem::path
shared_ptr
sub_match
thread::id
unique_ptr
Adding formatter specializations for all or most of these will reduce surprises for users that convert from stream centric output, to format centric output:
Before |
After |
And similar for most types, but also:
Before |
After |
Stream output of error_code
gives “category-name:integer-error-code” today. This proposal follows that, but an option would be to out put “category-name:category-message(integer-error-code)” instead.
bitset
operator<<
uses locale, for that reason this proposal suggest b.template to_string<charT>()
as the wanted output instead.
unique_ptr
and shared_ptr
use .get()
in operator<<
, so one option is to not add formatters for those since format
formatters are disabled for pointer types in general. The proposal adds formatters for the smart-pointers, but where the result from .get()
is cast to void*
before output.
This proposal does not suggest to use ostream
to format complex
, since stream output of complex
takes locale into account which we probably do not want. Also using stream rules to output the .imag()
and .real()
parts does not follow how format
outputs floats. complex{1.0, 2.0}
, output with streams gives “(1,2)
”, whereas format("({},{})", c.imag(), c.real()
gives “(1.0,2.0)
”. This proposal specifies a custom format-spec
that follows the std-format-spec
closely, where sign, alternate representation, precision and type applies to the inner T, and where fill, alignment and width applies to the value as a whole. The alignment option ‘=’ and preceding the width field with zero (‘0’) is not allowed.
Leave the basic_streambuf
alone and let ostream handle that.
Add formatter
specializations for:
error_code
bitset
unique_ptr
shared_ptr
complex
filesystem::path
sub_match
thread::id
OK with using format string specifications for the underlying type
Want format of complex to follow format("{}", 2.0)
output and not cout << 2.0
output?
Add the formatters for unique_ptr
and shared_ptr
even if formatting of pointers are normally disabled in std::format.
Inherit from the “underlying” type to get the format specification implicit from there, or drop the inheritance and put requirement on format specification in the normative test and leave it to the implementation how to get the format specification done?
Use auto
as return type from formatter functions instead of the verbose typename basic_format_context<Out, charT>::iterator
?
Add to 19.5.1 Header <system_error>
synopsis [system.error.syn] just below operator<<
:
Add a new section 19.5.3.6 Formatter [syserr.errcode.fmt]
template<class charT>
struct formatter<error_code, charT> : formatter<basic_string<charT>, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(error_code ec, basic_format_context<Out, charT> & ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(error_code ec, basic_format_context<Out, charT> & ctx);
Effects: As if implemented by:
basic_ostringstream<charT> o;
o.imbue(locale::classic());
o << ec;
return formatter<basic_string<charT>, charT>::format(o.str(), ctx);
Add to 20.9.1 Header <bitset>
synopsis [bitset.syn], just below operator<<
:
Add a new section
20.9.5 bitset formatter [bitset.fmt]
template<size_t N, class charT>
struct formatter<bitset<N>, charT> : formatter<basic_string<charT>, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const bitset<N>& b, basic_format_context<Out, charT>& ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const bitset<N>& b, basic_format_context<Out, charT>& ctx);
Effects: As if implemented by:
Add to 20.10.2 Header <memory>
synopsis [memory.syn] just after operator<<
:
Add a new section 20.11.1.7 Formatter [unique.ptr.fmt]
template<class T, class D, class charT>
struct formatter<unique_ptr<T, D>, charT> : formatter<void*, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const unique_ptr<T>& p, basic_format_context<Out, charT>& ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const unique_ptr<T>& p, basic_format_context<Out, charT>& ctx);
Constraints: p.get()
to be a valid expression.
Effects: As if implemented by:
Add to 20.10.2 Header <memory>
synopsis [memory.syn] just after operator<<
:
Add a new section 20.11.3.12 Formatter [util.smartptr.shared.fmt]
template<class T, class charT>
struct formatter<shared_ptr<T>, charT> : formatter<void*, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const shared_ptr<T>& p, basic_format_context<Out, charT>& ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const shared_ptr<T>& p, basic_format_context<Out, charT>& ctx);
Constraints: p.get()
to be a valid expression.
Effects: As if implemented by:
Add to 26.4.1 Header <complex>
synopsis [complex.syn] just below operator<<
:
Add a new section either after 26.4.10 or as part of 26.4.6 or as 26.4.7 (plus renumber following sections):
template<class T, class charT>
struct formatter<complex<T>, charT> {
typename basic_format_parse_context<charT>::iterator
parse(basic_format_parse_context & ctx);
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const complex<T>& c, basic_format_context<Out, charT>& ctx);
};
typename basic_format_parse_context::iterator
parse(basic_format_parse_context &);
Effects: Parses the format-spec
as per the std-format-spec
rules for T
, except:
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const complex<T>& c, basic_format_context<Out, charT>& ctx);
Effects: As if implemented by:
std::basic_string<charT> s =
std::format("({:[sign] ['#'] ['.' precision] [type]},"
"{:[sign] ['#'] ['.' precision] [type]})",
c.real(), c.imag());
return formatter<basic_string<charT>, charT>::format(
std::format("{:[[fill] align][width]}", s, ctx);
Add to 29.11.5 Header <filesystem>
synopsis [fs.filesystem.syn] (location up to editors discretion):
Add a new section 29.11.7.8 Formatter [fs.path.fmt]
template<class charT>
struct formatter<filesystem::path, charT> : formatter<basic_string<charT>, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const filesystem::path& p, basic_format_context<Out, charT>& ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const filesystem::path& p, basic_format_context<Out, charT>& ctx);
Effects: As if implemented by:
basic_ostringstream<charT> o;
o.imbue(locale::classic());
o << p;
return formatter<basic_string<charT>, charT>::format(o.str(), ctx);
Add to 30.4 Header <regex>
synopsis [re.syn] after the operator<<
:
Add a new section 30.9.3 [re.submatch.fmt]
template<class BiIter, class charT>
struct formatter<sub_match<BiIter>, charT> : formatter<basic_string<charT>, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const sub_match<BiIter>& s, basic_format_context<Out, charT>& ctx);
};
Constraints: is_same<sub_match<BiIter>::value_type, charT>
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(const sub_match<BiIter>& s, basic_format_context<Out, charT>& ctx);
Effects: As if implemented by:
Add to 32.3.2.1 Class thread::id [thread.thread.id] just after operator<<:
Add a new paragraph just after paragraph 12:
template<class charT>
struct formatter<thread::id, charT> : formatter<basic_string<charT>, charT> {
template<class Out>
typename basic_format_context<Out, charT>::iterator
format(thread::id id, basic_format_context<Out, charT>& ctx);
};
template<class Out>
typename basic_format_context<Out, charT>::iterator
format<thread::id id, basic_format_context<Out, charT>& ctx);
Effects: As if implemented by:
basic_ostringstream<charT> o;
o.imbue(locale::classic());
o << id;
return formatter<basic_string<charT>, charT>::format(o.str(), ctx);
All of the formatter specializations in this paper has been implemented on top of {fmt}.
Victor Zverovich, Jonathan Wakely and Juan Alday for looking through the draft and giving valuable comments and directions.