You need to be able to turn this...


						template<class... Args1, class... Args2>
						constexpr pair(piecewise_construct_t,
						               tuple<Args1...> first_args, tuple<Args2...> second_args);
					
18
Mandates:

(18.1)
  • is_constructible_v<T1, Args1...> is true and

(18.2)
  • is_constructible_v<T2, Args2...> is true


19
Effects: Initializes first with arguments of types Args1... obtained by forwarding the elements of first_args and initializes second with arguments of types Args2... obtained by forwarding the elements of second_args. (Here, forwarding an element x of type U within a tuple object means calling std::forward<U>(x).) This form of construction, whereby constructor arguments for first and second are each provided in a separate tuple object, is called piecewise construction.


[Note 2: If a data member of pair is of reference type and its initialization binds it to a temporary object, the program is ill-formed ([class.base.init]).— end note]

...into this


						template<class... Args1, class... Args2>
						constexpr pair(piecewise_construct_t,
						               tuple<Args1...> args1,
						               tuple<Args2...> args2)

						  noexcept(is_nothrow_constructible_v<T1, Args1...> and
						           is_nothrow_constructible_v<T2, Args2...>)

						: first(std::make_from_tuple<T1>(std::move(args1)))
						, second(std::make_from_tuple<T2>(std::move(args2)))
						{
							static_assert(is_constructible_v<T1, Args1...> and
						                is_constructible_v<T2, Args2...>);
						}
					


Correctness
(includes reliability, safety, and security)
Good performance

Some things we want as developers

Good diagnostics
## Good diagnostics ```cpp [] struct S {}; S s; std::print("{}", s); ``` notes: * A diagnostic is how the implementation communicates with the programmer * Often in the form of errors, warnings, and notes * The code on screen has a compile-time error * `std::print` hasn't been taught how to print `S` types yet

How you should feel when you get a diagnostic




😎





In file included from source.cpp:1:
In file included from /usr/bin/include/c++/v1/print:41:
In file included from /usr/bin/include/c++/v1/format:202:
/usr/bin/include/c++/v1/__format/format_functions.h:98:30: error: call to deleted constructor of 'formatter<S, char>'
  98 |       formatter<_Tp, _CharT> __f;
     |                             ^
/usr/bin/include/c++/v1/__format/format_functions.h:97:62: note: while substituting into a lambda expression here
  97 |     __parse_ = [](basic_format_parse_context<_CharT>& __ctx) {
     |                                                             ^
/usr/bin/include/c++/v1/__format/format_functions.h:392:25: note: in instantiation of function template specialization 'std::__format::__compile_time_handle<char>::__enable<S>' requested here
 392 |       __handle.template __enable<_Tp>();
     |                        ^
/usr/bin/include/c++/v1/__format/format_functions.h:388:99: note: while substituting into a lambda expression here
 388 |   static constexpr array<__format::__compile_time_handle<_CharT>, sizeof...(_Args)> __handles_{[] {
     |                                                                                                  ^
/usr/bin/include/c++/v1/__format/format_functions.h:372:54: note: in instantiation of static data member 'std::basic_format_string<char, S &>::__handles_' requested here
 372 |                            _Context{__types_.data(), __handles_.data(), sizeof...(_Args)});
     |                                                     ^
source.cpp:8:16: note: in instantiation of function template specialization 'std::basic_format_string<char, S &>::basic_format_string<char[3]>' requested here
   8 |     std::print("{}", s);
     |               ^
/usr/bin/include/c++/v1/__format/formatter.h:36:3: note: 'formatter' has been explicitly marked deleted here
  36 |   formatter()                            = delete;
     |  ^
In file included from source.cpp:1:
In file included from /usr/bin/include/c++/v1/print:41:
In file included from /usr/bin/include/c++/v1/format:202:
/usr/bin/include/c++/v1/__format/format_functions.h:99:28: error: no member named 'parse' in 'std::formatter<S>'
  99 |       __ctx.advance_to(__f.parse(__ctx));
     |                       ~~~ ^
source.cpp:8:16: error: call to consteval function 'std::basic_format_string<char, S &>::basic_format_string<char[3]>' is not a constant expression
   8 |     std::print("{}", s);
     |               ^
/usr/bin/include/c++/v1/__format/format_functions.h:271:7: note: non-constexpr function '__throw_format_error' cannot be used in a constant expression
 271 |       std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
     |      ^
/usr/bin/include/c++/v1/__format/format_functions.h:316:20: note: in call to '__handle_replacement_field<const char *, std::basic_format_parse_context<char>, std::__format::__compile_time_basic_format_context<char>>(&"{}"[1], &"{}"[2], basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)})'
 316 |         __begin  = __format::__handle_replacement_field(__begin, __end, __parse_ctx, __ctx);
     |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_functions.h:371:5: note: in call to '__vformat_to<std::basic_format_parse_context<char>, std::__format::__compile_time_basic_format_context<char>>(basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)})'
 371 |     __format::__vformat_to(basic_format_parse_context<_CharT>{__str_, sizeof...(_Args)},
     |    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 372 |                            _Context{__types_.data(), __handles_.data(), sizeof...(_Args)});
     |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
source.cpp:8:16: note: in call to 'basic_format_string<char[3]>("{}")'
   8 |     std::print("{}", s);
     |               ^~~~
/usr/bin/include/c++/v1/__format/format_error.h:38:52: note: declared here
  38 | _LIBCPP_NORETURN inline _LIBCPP_HIDE_FROM_ABI void __throw_format_error(const char* __s) {
     |                                                   ^
In file included from source.cpp:1:
In file included from /usr/bin/include/c++/v1/print:41:
In file included from /usr/bin/include/c++/v1/format:195:
In file included from /usr/bin/include/c++/v1/__format/container_adaptor.h:20:
In file included from /usr/bin/include/c++/v1/__format/range_default_formatter.h:23:
In file included from /usr/bin/include/c++/v1/__format/range_formatter.h:23:
In file included from /usr/bin/include/c++/v1/__format/format_context.h:18:
/usr/bin/include/c++/v1/__format/format_arg_store.h:167:17: error: static assertion failed due to requirement '__arg != __arg_t::__none': the supplied type is not formattable
 167 |   static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");
     |                ^~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg_store.h:214:54: note: in instantiation of function template specialization 'std::__format::__create_format_arg<std::format_context, S>' requested here
 214 |         basic_format_arg<_Context> __arg = __format::__create_format_arg<_Context>(__args);
     |                                                     ^
/usr/bin/include/c++/v1/__format/format_arg_store.h:249:19: note: in instantiation of function template specialization 'std::__format::__create_packed_storage<std::format_context, S>' requested here
 249 |         __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...);
     |                  ^
/usr/bin/include/c++/v1/__format/format_functions.h:70:10: note: in instantiation of member function 'std::__format_arg_store<std::format_context, S>::__format_arg_store' requested here
  70 |   return std::__format_arg_store<_Context, _Args...>(__args...);
     |         ^
/usr/bin/include/c++/v1/print:331:59: note: in instantiation of function template specialization 'std::make_format_args<std::format_context, S>' requested here
 331 |     __print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
     |                                                          ^
/usr/bin/include/c++/v1/print:341:8: note: in instantiation of function template specialization 'std::print<S &>' requested here
 341 |   std::print(stdout, __fmt, std::forward<_Args>(__args)...);
     |       ^
source.cpp:8:10: note: in instantiation of function template specialization 'std::print<S &>' requested here
   8 |     std::print("{}", s);
     |         ^
/usr/bin/include/c++/v1/__format/format_arg_store.h:167:23: note: expression evaluates to '0 != 0'
 167 |   static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");
     |                ~~~~~~^~~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg_store.h:169:17: error: static assertion failed
 169 |   static_assert(__formattable_with<_Tp, _Context>);
     |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg_store.h:169:17: note: because '__formattable_with<S, std::format_context>' evaluated to false
/usr/bin/include/c++/v1/__format/concepts.h:49:5: note: because 'std::formatter<S>' does not satisfy 'semiregular'
  49 |     semiregular<_Formatter> &&
     |    ^
/usr/bin/include/c++/v1/__concepts/semiregular.h:27:23: note: because 'std::formatter<S>' does not satisfy 'copyable'
  27 | concept semiregular = copyable<_Tp> && default_initializable<_Tp>;
     |                      ^
/usr/bin/include/c++/v1/__concepts/copyable.h:30:5: note: because 'std::formatter<S>' does not satisfy 'copy_constructible'
  30 |     copy_constructible<_Tp> &&
     |    ^
/usr/bin/include/c++/v1/__concepts/constructible.h:45:5: note: because 'std::formatter<S>' does not satisfy 'move_constructible'
  45 |     move_constructible<_Tp> &&
     |    ^
/usr/bin/include/c++/v1/__concepts/constructible.h:39:30: note: because 'constructible_from<std::formatter<S>, std::formatter<S> >' evaluated to false
  39 | concept move_constructible = constructible_from<_Tp, _Tp> && convertible_to<_Tp, _Tp>;
     |                             ^
/usr/bin/include/c++/v1/__concepts/constructible.h:27:51: note: because 'is_constructible_v<std::formatter<S>, std::formatter<S> >' evaluated to false
  27 | concept constructible_from = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
     |                                                  ^
In file included from source.cpp:1:
In file included from /usr/bin/include/c++/v1/print:41:
In file included from /usr/bin/include/c++/v1/format:195:
In file included from /usr/bin/include/c++/v1/__format/container_adaptor.h:20:
In file included from /usr/bin/include/c++/v1/__format/range_default_formatter.h:23:
In file included from /usr/bin/include/c++/v1/__format/range_formatter.h:23:
In file included from /usr/bin/include/c++/v1/__format/format_context.h:18:
/usr/bin/include/c++/v1/__format/format_arg_store.h:205:12: error: no matching constructor for initialization of 'basic_format_arg<format_context>'
 205 |     return basic_format_arg<_Context>{__arg, __value};
     |           ^                         ~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg.h:350:34: note: candidate constructor not viable: no known conversion from 'S' to '__basic_format_arg_value<format_context>' for 2nd argument
 350 |   _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(__format::__arg_t __type,
     |                                 ^
 351 |                                                   __basic_format_arg_value<_Context> __value) noexcept
     |                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg.h:278:28: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 2 were provided
 278 | class _LIBCPP_TEMPLATE_VIS basic_format_arg {
     |                           ^~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg.h:278:28: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 2 were provided
 278 | class _LIBCPP_TEMPLATE_VIS basic_format_arg {
     |                           ^~~~~~~~~~~~~~~~
/usr/bin/include/c++/v1/__format/format_arg.h:282:25: note: candidate constructor not viable: requires 0 arguments, but 2 were provided
 282 |   _LIBCPP_HIDE_FROM_ABI basic_format_arg() noexcept : __type_{__format::__arg_t::__none} {}
     |                        ^
6 errors generated.
Compiler returned: 1
					

👀

How do we achieve these goals?

Act III

General engineering practices

Unit tests


Rewiring your brain
With Test Driven Thinking (in C++)
All Your Tests are Terrible:
Tales from the Trenches


Output with AddressSanitizer


=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000020 ...
READ of size 4 at 0x502000000020 thread T0
    #0 0x558c7e4ce923 in main /tmp/example.cpp:6:12
    #1 0x7f48e5e29d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
    #2 0x7f48e5e29e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
    #3 0x558c7e3f63b4 in _start (/tmp/output.s+0x2c3b4)
...
				
Build with `-fsanitize=address` globally, for libc++ and MSVC's STL

Build with `-fsanitize=address -D_GLIBCXX_SANITIZE_VECTOR` globally, for libstdc++

Hardened/assertion builds


						constexpr T& operator*() & noexcept {
							_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
								has_value(),
								"optional operator* called on a disengaged value");
								return get();
							}
					

						.../optional:811: assertion has_value() failed:
															optional operator* called on a disengaged value
						Program terminated with signal: SIGILL
					
libc++ / libstdc++ / MSVC's STL

Turns out that it's undefined


						// [rand.dist.uni.int], class template uniform_int_distribution
						template<class IntType = int>
						  class uniform_int_distribution;
					

1
Throughout this subclause [rand], the effect of instantiating a template:

...
(1.5)
  • that has a template type parameter named IntType is undefined unless the corresponding template argument is cv-unqualified and is one of short, int, long, long long, unsigned short, unsigned int, unsigned long, or unsigned long long.



Undefined ⇒ badness

Undefined ⇒ implementation decides if valid

3.62
undefined behavior

behavior for which this document imposes no requirements

[Note 1: Undefined behavior may be expected when this document omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message ([defns.diagnostic])), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. Evaluation of a constant expression ([expr.const]) never exhibits behavior explicitly specified as undefined in [intro] through [cpp].— end note]

Undefined ⇒ badness

Undefined ⇒ implementation decides if valid

3.62
undefined behavior

behavior for which this document imposes no requirements

[Note 1: Undefined behavior may be expected when this document omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message ([defns.diagnostic])), to terminating a translation or execution (with the issuance of a diagnostic message). Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed. Evaluation of a constant expression ([expr.const]) never exhibits behavior explicitly specified as undefined in [intro] through [cpp].— end note]

Possible responses to Hyrum's law


Prioritise stability over progress

Prioritise progress over stability


Permanent
rollback

Breaking any user is unacceptable

Temporary
rollback

Users need to fix in a timely manner

Tiered release over three versions
  1. deprecate + opt into change
  2. opt out of change
  3. old behaviour removed

Forward-fix
prioritised

Temporary user pain so others have their needs met

Nothing to fix

Not enough users impacted, or
change too important to revert

Possible responses to Hyrum's law


Prioritise stability over progress

Prioritise progress over stability


Permanent
rollback

Breaking any user is unacceptable

Temporary
rollback

Users need to fix in a timely manner

Tiered release over three versions
  1. deprecate + opt into change
  2. opt out of change
  3. old behaviour removed

Forward-fix
prioritised

Temporary user pain so others have their needs met

Nothing to fix

Not enough users impacted, or
change too important to revert

How do we prevent Hyrum's law?

Simulate user input with fuzzing


					// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
					// See https://llvm.org/LICENSE.txt for license information.
					extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data,
																								std::size_t size) {
							std::vector<std::uint8_t> working(data, data + size);
							std::sort(working.begin(), working.end());

							if (!std::is_sorted(working.begin(), working.end()))
									return 1;
							// some more checks...
							return 0;
					}
					
libc++ example

Use integration tests to catch Hyrumbugs

Identify who gets broken early.
Build as many packages on as many platforms as you can.

libc++

FreeBSD
Gentoo

libstdc++

Debian
Fedora
Gentoo

MSVC's STL

Handled by the release team

Act III

Gear Second

Case study: std::vector

only talk about performance when you have

benchmarks

What does any of this have to do with implementing the standard library?

Everything!

What people think it's like

People scaling Hillary Step, near the top of Mt. Everest.
By Debasish biswas kolkataDerivative work MagentaGreen - This file was derived from: Hillary Step near Everest top.jpg:, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=62662103

What it's actually like

People taking the Mt. Kosciuszko Summit Walk in NSW, Australia.
By AS, 'Path to Mt Kosciuszko', CC BY 2.0, https://www.flickr.com/photos/aschaf/4492775854/in/photostream/

Act III

Understand what you're getting yourself into

What is the standard library?



The headers that ship with the compiler
(e.g. <vector>)

The body of text from [library] through [thread] in the C++ standard

🤝

It's in std

(or libc)


Don't use cppreference

😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱
😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱😱


								template< class... Args1, class... Args2 >
								pair( std::piecewise_construct_t,
								      std::tuple<Args1...> first_args,
								      std::tuple<Args2...> second_args );
							


(since C++11)
(until C++20)


(9)


								template< class... Args1, class... Args2 >
								constexpr pair( std::piecewise_construct_t,
								                std::tuple<Args1...> first_args,
								                std::tuple<Args2...> second_args );
							


(since C++20)
9)
Forwards the elements of first_args to the constructor of first and forwards the elements of second_args to the constructor of second. This is the only non-default constructor that can be used to create a pair of non-copyable non-movable types. The program is ill-formed if first or second is a reference and bound to a temporary object.

Standardese


						template<class... Args1, class... Args2>
						constexpr pair(piecewise_construct_t,
						               tuple<Args1...> first_args, tuple<Args2...> second_args);
					
18
Mandates:

(18.1)
  • is_constructible_v<T1, Args1...> is true and

(18.2)
  • is_constructible_v<T2, Args2...> is true


19
Effects: Initializes first with arguments of types Args1... obtained by forwarding the elements of first_args and initializes second with arguments of types Args2... obtained by forwarding the elements of second_args. (Here, forwarding an element x of type U within a tuple object means calling std::forward<U>(x).) This form of construction, whereby constructor arguments for first and second are each provided in a separate tuple object, is called piecewise construction.


[Note 2: If a data member of pair is of reference type and its initialization binds it to a temporary object, the program is ill-formed ([class.base.init]).— end note]


(3.2)
— Mandates: the conditions that, if not met, render the program ill-formed.

     [Example 2: An implementation can express such a condition via the constant-expression in a static_assert-declaration
     ([dcl.pre]). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an
     implementation can express such a condition via a constraint-expression ([temp.constr.decl]) and also define the function
     as deleted. — end example]







(3.1)
— Constraints: the conditions for the function's participation in overload resolution ([over.match]).

     [Note 1: Failure to meet such a condition results in the function's silent non-viability. — end note]

     [Example 1: An implementation can express such a condition via a constraint-expression ([temp.constr.decl]). — end example]

Compare the pair


Based off cppreference


					template<class... Args1, class... Args2>
					requires is_constructible_v<T1, Args1...> and
					         is_constructible_v<T2, Args2...>
					pair(piecewise_construct_t, tuple<Args1...> args1, tuple<Args2...> args2)
					: first(std::make_from_tuple(std::move(args1)))
					, second(std::make_from_tuple(std::move(args2)))
					{}
					

Based off standardese


						template<class... Args1, class... Args2>
						pair(piecewise_construct_t, tuple<Args1...> args1, tuple<Args2...> args2)
						: first(std::make_from_tuple(std::move(args1)))
						, second(std::make_from_tuple(std::move(args2)))
						{
							static_assert(is_constructible_v<T1, Args1...>);
							static_assert(is_constructible_v<T2, Args2...>);
						}
					

								explicit vector( size_type count,
								                  const T& value = T(),
								                  const Allocator& alloc = Allocator() );
							


(until C++11)



								vector( size_type count,
								        const T& value,
								        const Allocator& alloc = Allocator() );
							
(3)


(since C++11)
(until C++20)



								constexpr vector( size_type count,
								                  const T& value,
								                  const Allocator& alloc = Allocator() );
							


(since C++20)


Constructs a new container from a variety of data sources, optionally using a user supplied allocator alloc.
...

3)
Constructs the container with count copies of elements with value value.


Complexity



3,4)
Linear in count

Standardese


						constexpr vector(size_type n, const T& value,
						                 const Allocator& = Allocator());
					
6
Preconditions: T is Cpp17CopyInsertable into *this.

7
Effects: Constructs a vector with n copies of value, using the specified allocator.

8
Complexity: Linear in n.

Don't use cppreference

Only consult the standard when implementing C++


Great for user code

Required for compiler/stdlib work


cppreference

MSDN

Blogs, books, etc.


The same applies to reviewing contributions.

What is the C++ implementation?

What is the C++ implementation?

Standard library implementers are
compiler developers


						$ apt install clang-18 libc++-18-dev libc++abi-18-dev
					

Direct input from the compiler can be unavoidable


General language features (e.g. modules for module std;)
std::is_trivially_copyable_v<T>
std::is_constant_evaluated()
std::source_location
All atomic operations
Coroutines

Direct input from the
compiler can be optimal


Clang-supported

Hopefully soon


std::move, etc.
std::invoke

std::make_integer_sequence
std::tuple's guts

std::printf's interface
std::format's interface


Most of the standard library should still
be implemented in C++ code


Algorithms
Containers
Iterators
Ranges
I/O logic
Stacktrace support
Algebraic data types

Act IV

The exclusivity of
the standard library

Remember: we want good diagnostics!


						struct S {};
						S s;
						std::print("{}", s);
					

In file included from source.cpp:1:
In file included from /usr/include/c++/14.0.1/print:41:
/usr/include/c++/14.0.1/format:4030:3: error: static assertion failed due to requirement 'is_default_constructible_v<std::formatter<S, char>>': std::formatter must be specialized for each type being formatted
 4030 |         (is_default_constructible_v<formatter<_Args, _CharT>> && ...),
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/14.0.1/format:4174:4: note: in instantiation of template class 'std::__format::_Checking_scanner<char, S>' requested here
 4174 |           __scanner(_M_str);
      |           ^
source.cpp:8:16: note: in instantiation of function template specialization 'std::basic_format_string<char, S &>::basic_format_string<char[3]>' requested here
    8 |            std::print("{}", s);
      |                       ^
source.cpp:8:16: error: call to consteval function 'std::basic_format_string<char, S &>::basic_format_string<char[3]>' is not a constant expression
    8 |            std::print("{}", s);
      |                       ^
In file included from source.cpp:1:
In file included from /usr/include/c++/14.0.1/print:41:
/usr/include/c++/14.0.1/format:3711:31: error: no matching constructor for initialization of 'basic_format_arg<basic_format_context<_Sink_iter<char>, char>>'
 3711 |           basic_format_arg<_Context> __arg(__v);
      |                                      ^     ~~~
/usr/include/c++/14.0.1/format:3722:12: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_S_make_elt<S>' requested here
 3722 |         : _M_args{_S_make_elt(__a)...}
      |                   ^
/usr/include/c++/14.0.1/format:3772:14: note: in instantiation of function template specialization 'std::__format::_Arg_store<std::basic_format_context<std::__format::_Sink_iter<char>, char>, std::basic_format_arg<std::basic_format_context<std::__format::_Sink_iter<char>, char>>::handle>::_Arg_store<S>' requested here
 3772 |       return _Store(__fmt_args...);
      |              ^
/usr/include/c++/14.0.1/print:116:12: note: in instantiation of function template specialization 'std::print<S &>' requested here
  116 |     { std::print(stdout, __fmt, std::forward<_Args>(__args)...); }
      |            ^
source.cpp:8:10: note: in instantiation of function template specialization 'std::print<S &>' requested here
    8 |            std::print("{}", s);
      |                 ^
/usr/include/c++/14.0.1/format:3197:11: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S' to 'const basic_format_arg<basic_format_context<_Sink_iter<char>, char>>' for 1st argument
 3197 |     class basic_format_arg
      |           ^~~~~~~~~~~~~~~~
/usr/include/c++/14.0.1/format:3197:11: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'S' to 'basic_format_arg<basic_format_context<_Sink_iter<char>, char>>' for 1st argument
 3197 |     class basic_format_arg
      |           ^~~~~~~~~~~~~~~~
/usr/include/c++/14.0.1/format:3458:2: note: candidate template ignored: constraints not satisfied [with _Tp = S]
 3458 |         basic_format_arg(_Tp& __v) noexcept
      |         ^
/usr/include/c++/14.0.1/format:3456:11: note: because '__format::__formattable_with<S, std::basic_format_context<std::__format::_Sink_iter<char>, char> >' evaluated to false
 3456 |         requires __format::__formattable_with<_Tp, _Context>
      |                  ^
/usr/include/c++/14.0.1/format:2503:9: note: because 'std::formatter<S>' does not satisfy 'semiregular'
 2503 |       = semiregular<_Formatter>
      |         ^
/usr/include/c++/14.0.1/concepts:280:27: note: because 'std::formatter<S>' does not satisfy 'copyable'
  280 |     concept semiregular = copyable<_Tp> && default_initializable<_Tp>;
      |                           ^
/usr/include/c++/14.0.1/concepts:275:24: note: because 'std::formatter<S>' does not satisfy 'copy_constructible'
  275 |     concept copyable = copy_constructible<_Tp> && movable<_Tp>
      |                        ^
/usr/include/c++/14.0.1/concepts:179:9: note: because 'std::formatter<S>' does not satisfy 'move_constructible'
  179 |       = move_constructible<_Tp>
      |         ^
/usr/include/c++/14.0.1/concepts:174:7: note: because 'constructible_from<std::formatter<S>, std::formatter<S> >' evaluated to false
  174 |     = constructible_from<_Tp, _Tp> && convertible_to<_Tp, _Tp>;
      |       ^
/usr/include/c++/14.0.1/concepts:160:30: note: because 'is_constructible_v<std::formatter<S>, std::formatter<S> >' evaluated to false
  160 |       = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;
      |                              ^
/usr/include/c++/14.0.1/format:3258:7: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
 3258 |       basic_format_arg() noexcept : _M_type(__format::_Arg_none) { }
      |       ^
3 errors generated.
Compiler returned: 1
					

👀

Not great, but could be worse...


example.cpp
C:/data/msvc/14.39.33321-Pre/include\format(687): error C2672: 'std::_Format_arg_traits<_Context>::_Type_eraser': no matching overloaded function found
        with
        [
            _Context=std::format_context
        ]
C:/data/msvc/14.39.33321-Pre/include\format(684): note: could be 'auto std::_Format_arg_traits<_Context>::_Type_eraser(void)'
        with
        [
            _Context=std::format_context
        ]
C:/data/msvc/14.39.33321-Pre/include\format(687): note: the associated constraints are not satisfied
C:/data/msvc/14.39.33321-Pre/include\format(847): note: the concept 'std::_Formattable_with<S,std::format_context,std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\format(661): note: the concept 'std::semiregular<std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\concepts(255): note: the concept 'std::copyable<std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\concepts(248): note: the concept 'std::copy_constructible<std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\concepts(170): note: the concept 'std::move_constructible<std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\concepts(105): note: the concept 'std::constructible_from<std::formatter<S,_CharT>,std::formatter<S,_CharT>>' evaluated to false
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\concepts(95): note: the constraint was not satisfied
C:/data/msvc/14.39.33321-Pre/include\format(687): note: the template instantiation context (the oldest one first) is
<source>(8): note: see reference to function template instantiation 'std::basic_format_string<char,S &>::basic_format_string<char[3]>(const _Ty (&))' being compiled
        with
        [
            _Ty=char [3]
        ]
C:/data/msvc/14.39.33321-Pre/include\format(3802): note: see reference to class template instantiation 'std::__p2286::_Format_checker<_CharT,S>' being compiled
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\format(3579): note: while compiling class template member function 'std::__p2286::_Format_checker<_CharT,S>::_Format_checker(std::basic_string_view<char,std::char_traits<char>>) noexcept'
        with
        [
            _CharT=char
        ]
C:/data/msvc/14.39.33321-Pre/include\format(3580): note: see reference to function template instantiation 'std::_String_view_iterator<_Traits> std::__p2286::_Compile_time_parse_format_specs<S,std::basic_format_parse_context<char>>(_ParseContext &)' being compiled
        with
        [
            _Traits=std::char_traits<char>,
            _ParseContext=std::basic_format_parse_context<char>
        ]
C:/data/msvc/14.39.33321-Pre/include\format(3559): note: see reference to alias template instantiation 'std::_Format_arg_traits<_Context>::_Storage_type<S>' being compiled
        with
        [
            _Context=std::format_context
        ]
C:/data/msvc/14.39.33321-Pre/include\format(3563): error C2993: 'unknown-type': is not a valid type for non-type template parameter '_Test'
C:/data/msvc/14.39.33321-Pre/include\format(3565): error C2641: cannot deduce template arguments for 'std::formatter'
C:/data/msvc/14.39.33321-Pre/include\format(3565): error C2783: 'std::formatter<_Ty,_CharT> std::formatter(void)': could not deduce template argument for '_Ty'
C:/data/msvc/14.39.33321-Pre/include\format(3648): note: see declaration of 'std::formatter'
C:/data/msvc/14.39.33321-Pre/include\format(3565): error C2780: 'std::formatter<_Ty,_CharT> std::formatter(std::formatter<_Ty,_CharT>)': expects 1 arguments - 0 provided
C:/data/msvc/14.39.33321-Pre/include\format(3647): note: see declaration of 'std::formatter'
C:/data/msvc/14.39.33321-Pre/include\format(3566): error C2039: 'parse': is not a member of 'std::formatter'
C:/data/msvc/14.39.33321-Pre/include\format(3647): note: see declaration of 'std::formatter'
Compiler returned: 2
					

... a lot worse


source.cpp:8:10: error: no member named 'print' in namespace 'std'
    8 |     std::print("{}", s);
      |     ~~~~~^
					

Can we do better?


						struct S {};
						auto const s = S{};
						std::printf("%d", s);
					

						source.cpp:8:23: warning: format specifies type 'int' but the argument has type 'const S' [-Wformat]
						   8 |     std::printf("%d", s);
						     |                 ~~   ^
					

What do you think?


						struct S {};
						auto const s = S{};
						std::print("{}", s);
					

						source.cpp:8:22: error: type 'S' does not have a format specifier
						   8 |     std::print("{}", s);
						     |                 ~~   ^
						source.cpp:8:23: note: a partial or explicit specialization for class template 'std::formatter<S, char>' is required
					

Ideal scenario in the current framework?


						source.cpp:8:22: error: type 'S' does not have a format specifier
						   8 |     std::print("{}", s);
						     |                 ~~   ^
						source.cpp:8:23: note: a partial or explicit specialization for class template 'std::formatter<S, char>' is required
						     |     // sample suggestion
						     |     template<>
						     |     struct std::formatter<S, char> {
						     |       template<class ParseContext>
						     |       constexpr typename ParseContext::iterator parse(ParseContext &c) {
						     |         return c.begin();
						     |       }
						     |
						     |       template<class FormatContext>
						     |       typename FormatContext::iterator format(S s, FormatContext &c) const {
						     |         return std::ranges::copy("Shishishi!", c.out()).out;
						     |       }
						     |     };
					

Compiler extensions are good

for the standard library to use on your behalf

Act V

We would be honoured
if you would join us

The compiler and runtime are inextricably linked

and this is a good thing

Our engineering practices are the same as yours

A lot of tenacity

Join our crew!

Onboarding pages:


https://slides.cjdb.xyz