1. Proposal
This paper proposes that source_location from Library Fundamentals V3 [N4758] be adopted into the C++20 standard.
has been successfully implemented without difficulties in GCC, and Clang implementers almost completed their implementation of it.
As described by the original paper, it’s a necessary feature to implement logging without requiring macros, one that cannot be portably implemented
by non-standard libraries because it requires compiler support.
has been very favorably received by both EWG and LEWG [n4129], [n4417] and has been part of library
fundamentals v2 [n4562] and v3 [N4758] since 2015 without changes to exposed interface.
Note: A proposal [P0555R0] in 2017 to make use of
in
has been withdrawn by their authors once
gained a constexpr constructor, allowing the use of
in constexpr contexts.
2. A few design changes
During an early review of this papers, some interest in a few design changes arose:
2.1. Enforcing a size for source_location
objects ?
No consensus in San Diego
as currently implemented by Clang and GCC is a structure encapsulating all its members (file, function, line, column),
and so its size is roughly
.
A note states:
[ Note: The intent of source_location is to have a small size and efficient copying. — end note ]
However, there seems to be some interest for having
be guaranteed
.
Notably, reviewers wonder if
should be embedded in a yet to be proposed
.
Herb Sutter and Niall Douglas pointed out that
is meant to handle recoverable errors,
rather than to communicate an error to developers (see Herb Sutter’s talk on error handling CppCon 2018).
And while we agree strongly with this sentiment, it’s worth noting that a subset of the C committee
is interested in having source information in whatever deterministic exception handling they are working towards;
While, as pointed out by Niall, it’s unlikely the two languages will have directly interoperable mechanisms,
having a guaranteed size for
might make such interoperability easier should a need for it arise.
Barring that,
is an error reporting tool targeted at human developers,
and the cost of any such error reporting system is many orders of magnitude larger than copying
3 pointers around. We do not think there is a strong incentive to guarantee the size of that type
or restricting the way it is implemented.
For completeness, such modification could be achieved by adding one level of indirection:
struct __source_location_data { /* implementation defined */ }; struct source_location { private : const __source_location_data * __data ; };
Alternatively, source_location could return a const reference:
struct source_location { constexpr const source_location & current () noexcept ; };
The authors strongly prefer the first solution as we think retaining value semantics is important. It is also important to note that, while not implemented, the first solution has been considered and is deemed realistically implementable by both GCC and Clang developers (Jonathan Wakely, Eric Fiselier).
2.2. Removing current ()
altogether ?
No consensus in San Diego
If LEWG elects to keep value semantics, the authors would like to propose that the
function is removed,
since, in its current form,
has a default constructor that has no meaningful use.
Besides being harder to misuse, using the constructor of the type to acquire its value is also significantly less verbose.
void log ( auto && message , std :: source_location sc = std :: source_location :: current ()); //or void log ( auto && message , std :: source_location sc = {});
3. User-constructed source_location
In San Diego, it was suggested to add the following constructorSo that source_location can be constructed by users and other libraries (stacktrace ?) However,consteval source_location ( uint_least32_t line , uint_least32_t column , const char * filename , const char * function );
consteval
, to the best of our understanding doesn’t not guarantee that filename
and function
will
live past the call to that constructor.
and
would have to be copied (at compile time)
and it isn’t clear wether it would be possible to copy them in read only memory.
Therefore we suggested that this could be the subject of a separate paper to explore these lifetime issues
4. Proposed Wording
The following wording correspond to the wording as in [N4758] (Library fundamental), with the exception of making source_location::current() an immediate function as decided by LEWG in San Diego.
Notes To The Editor:
- The <source_location> header in Library fundamental is part of a "Reflection" library that does not yet exist and may need to be created
or be placed elsewhere.
- The <source_location> header should be added to the list of freestanding headers
namespace std { struct source_location { static consteval source_location current () noexcept ; constexpr source_location () noexcept ; constexpr uint_least32_t line () const noexcept ; constexpr uint_least32_t column () const noexcept ; constexpr const char * file_name () const noexcept ; constexpr const char * function_name () const noexcept ; }; } // namespace std
is to have a small size and efficient copying. — end note ]
static consteval source_location current () noexcept ;
- Returns:
- When invoked by a function call (
C++17 §8.2.2 ) whosepostfix-expression is a (possibly parenthesized)id-expression naming
, returns acurrent
with an implementation-defined value. The value should be affected bysource_location
(#line C++17 §19.4 ) in the same manner as for
and__LINE__
. If invoked in some other way, the value returned is unspecified.__FILE__ - Remarks:
- When a
brace-or-equal-initializer is used to initialize a non-static data member, any calls to
should correspond to the location of the constructor or aggregate initialization that initializes the member.current [ Note: When used as a default argument ( C++17 §11.3.6 ), the value of the
will be the location of the call tosource_location
at the call site. — end note ]current [ Example:
struct s { source_location member = source_location :: current (); int other_member ; s ( source_location loc = source_location :: current ()) : member ( loc ) // values of member will be from call-site {} s ( int blather ) : // values of member should be hereabouts other_member ( blather ) {} s ( double ) // values of member should be hereabouts {} }; void f ( source_location a = source_location :: current ()) { source_location b = source_location :: current (); // values in b represent this line } void g () { f (); // f ’s first argument corresponds to this line of code source_location c = source_location :: current (); f ( c ); // f ’s first argument gets the same values as c , above } — end example ]
constexpr source_location () noexcept ;
- Effects:
- Constructs an object of class
.source_location - Remarks:
- The values are implementation-defined.
constexpr uint_least32_t line () const noexcept ;
- Returns:
- The presumed line number (
C++17 §19.8 ) represented by this object.
constexpr uint_least32_t column () const noexcept ;
- Returns:
- An implementation-defined value representing some offset from the start of the line represented by this object.
constexpr const char * file_name () const noexcept ;
- Returns:
- The presumed name of the current source file (
C++17 §19.8 ) represented by this object as an NTBS.
constexpr const char * function_name () const noexcept ;
- Returns:
- If this object represents a position in the body of a function, returns an implementation-defined NTBS that should correspond to the function name. Otherwise, returns an empty string.
5. Feature macro
Similarly to the original [n4417] wording, we recommend the feature test macro__cpp_lib_source_location
for this feature.
6. Acknowledgments
The authors would like to thanks the people who reviewed early version of this proposal, notably Peter Dimov, Jonathan Wakely Arthur O’Dwyer and Geoffrey Romer. We would also like to thank Herb Sutter and Niall Douglas for their insightful remarks on std::error and errors handling in general.