1. Introduction
This paper proposes making and publicly derived exception
types formattable with and printable with .
It also specifies the encoding of library-generated parts of standard exception
messages, resolving [LWG4087].
2. Motivation
Exception messages are commonly written to logs or diagnostic output. Today
this requires explicitly calling :
try { process_request (); } catch ( const std :: exception & e ) { std :: println ( stderr , "Request failed: {}" , e . what ()); }
Unfortunately, this does not work reliably. Consider the following example from [LWG4087]:
std :: uintmax_t size = 0 ; try { size = std :: filesystem :: file_size ( L"Шчучыншчына" ); } catch ( const std :: exception & e ) { std :: println ( "Памылка: {}" , e . what ()); }
This gives the following output on Windows even when the literal encoding is UTF-8 and the regional settings are set to Belarusian (the language of the text):
Памылка : file_size : The system cannot find the file specified . : "�����������"
Microsoft STL implements using
and first requests an en-US message using language identifier . Since
the English message is ASCII, only the filesystem path exhibits mojibake.
Trying to do the same with iostreams
gives even worse result:std :: cout << "Памылка: " << e . what () << std :: endl ;
╨Я ╨░╨╝╤Л ╨╗╨║╨░: file_size : The system cannot find the file specified . : "╪ўєў√э°ў√эр"
The situation is somewhat better on macOS and most Linux systems where UTF-8 has become de-facto standard.
The current proposal makes directly formattable and specifies
a consistent encoding for messages produced by the standard library
implementation itself, making both
andstd :: println ( "Памылка: {}" , e );
work and produce the correct output such asstd :: println ( "Памылка: {}" , e . what ());
(The exact message format depends on the implementation.)Памылка : file_size : The system cannot find the file specified . : Шчучыншчына
3. Proposal
The current paper proposes a partial specialization for
and types publicly derived from it. The default format is the
string returned by the virtual member function. The specialization
uses the standard string format specification, providing the usual fill,
alignment, width, precision, and debug formatting. It also resolves [LWG4087]
by specifying the encoding of library-generated exception messages.
For example:
std :: runtime_error e ( "connection refused" ); auto s1 = std :: format ( "{}" , e ); // "connection refused" auto s2 = std :: format ( "{:>20}" , e ); // " connection refused" auto s3 = std :: format ( "{:?}" , e ); // "\"connection refused\""
The formatter is not opted into the nonlocking formatter optimization because
is virtual and can execute program-defined code.
3.1. Encoding
The runtime encoding of is currently unspecified, making portable
interpretation and display impossible in general. The encoding problem cannot
be solved in the formatter: by the time it calls , differently encoded
components may already have been combined into a single NTBS with no information
about their source encodings. The message must instead be normalized when it is
constructed.
For the existing interface, the ordinary literal
encoding is the only choice that makes library-generated messages directly
composable with ordinary narrow strings on every implementation and fixes both
direct and formatted output. This is consistent with the design of
and , whose narrow-character interfaces use the
ordinary literal encoding. It also matches how exception messages are formed:
most are either constructed from string literals or contain literal
components.
Unlike the C locale encoding, the ordinary literal encoding does not change in
response to while an exception object is alive. This prevents bugs
where a message is constructed under one C locale and interpreted under
another.
The choice is also implementable with existing facilities. [P2319] fixed
filesystem path presentation problems by adding
, which produces a string suitable for
formatting and printing in the ordinary literal encoding. No corresponding API
converts a path to the C locale encoding.
A known, stable source encoding also enables future formatting support for
additional code unit types. Program-supplied bytes are preserved:
when they use the ordinary literal encoding, the complete message does too;
otherwise only the library-generated parts have that guarantee. Program-defined
exception types remain responsible for their own encoding but now have a
consistent standard-library model to follow. The formatter is currently
provided only for because returns a narrow string.
4. Wording
Modify paragraph 2 of [exception] as indicated:
The member function of each such satisfies the constraints
specified for (see below).
what () on an object whose dynamic type is a standard library
class that is exception or is derived from exception , every character
sequence incorporated into the returned NTBS, other than an NTBS supplied by
the program and copied without modification, is in the ordinary literal
encoding. If every such copied NTBS is in the ordinary literal encoding, the
returned NTBS is in the ordinary literal encoding.
Modify in [exception] as indicated:
constexpr virtual const char * what () const noexcept ;
Returns: An implementation-defined NTBS, which during constant evaluation is encoded with the ordinary literal encoding.
Remarks:
The message may be a null-terminated multibyte string, suitable
for conversion and display as a
The return value remains valid
until the exception object from which it is obtained is destroyed or a
non-. member function of the exception object is called.
Modify in [syserr.syserr.members] as indicated:
const char * what () const noexcept override ;
Returns: An NTBS incorporating the arguments supplied in the constructor.
For a constructor with awhat_arg parameter, the NTBS designated by
what_arg . c_str () or what_arg , as applicable, is incorporated without
changing its element values. If an error-category message is incorporated, then
during construction let m be code (). message () and let E be the encoding
of the C locale in effect when that call returns. m is
transcoded from E to the ordinary literal encoding during construction and
incorporated into the returned NTBS. The handling of characters not
representable in the ordinary literal encoding is implementation-defined.
Modify in
[fs.filesystem.error.members] as indicated:
const char * what () const noexcept override ;
Returns: An NTBS that incorporates the
NTBS designated by argument supplied to
the constructor without
changing its element values
. The exact format is unspecified.
Implementations should include the string and the
pathnames of and in the native format in the returned
string
in the returned string. For each included pathname
corresponding to a path , is evaluated during
construction and its result is incorporated into the returned string
.
The feature-test macro below detects the formatter addition. The encoding changes are intended to be applied as a defect report and do not depend on the macro.
Add an entry for to section "Header
synopsis" [version.syn], in a place that
respects the table’s current alphabetic order:
#define __cpp_lib_format_exception 20XXXXL // also in <format>
Modify "Header synopsis" [format.syn] as indicated:
// [format.formatter], formatter template < class T , class charT = char > struct formatter ; // [format.exception], exception formatter template < class T > requires derived_from < T , exception > struct formatter < T , char > ; // [format.formatter.locking], formatter locking template < class T > constexpr bool enable_nonlocking_formatter_optimization = false; template < class T > requires derived_from < T , exception > constexpr bool enable_nonlocking_formatter_optimization < T > = false;
Add a new section "Exception formatter" [format.exception] under [format.formatter]:
template < class T > requires derived_from < T , exception > struct formatter < T , char > : formatter < const char * , char > { template < class FormatContext > constexpr typename FormatContext :: iterator format ( const T & e , FormatContext & ctx ) const ; };
The specialization is debug-enabled and constexpr-enabled.
template < class FormatContext > constexpr typename FormatContext :: iterator format ( const T & e , FormatContext & ctx ) const ;
Returns: .
5. Implementation experience
A formatter for itself, as well as types derived from it, is
available in the open-source {fmt} library ([FMT]).