[libc++][format] Exposes basic-format-string

This paper was accepted during the last plenary and is intended to be
backported to LLVM 15. When backporting the release notes in the branch
should be updated too.

Note the feature-test macro isn't updated since this will change; three
papers have updated the same macro in the same plenary.

Implements:
- P2508R1 Exposing std::basic-format-string

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D130643
This commit is contained in:
Mark de Wever 2022-07-15 07:42:17 +02:00
parent 3e43d0cde7
commit f712775daf
8 changed files with 210 additions and 47 deletions

View File

@ -81,7 +81,7 @@
"`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","",""
"`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0"
"`P2502R2 <https://wg21.link/P2502R2>`__","LWG","``std::generator``: Synchronous Coroutine Generator for Ranges","July 2022","",""
"`P2508R1 <https://wg21.link/P2508R1>`__","LWG","Exposing ``std::basic-format-string``","July 2022","",""
"`P2508R1 <https://wg21.link/P2508R1>`__","LWG","Exposing ``std::basic-format-string``","July 2022","|Complete|","15.0"
"`P2513R4 <https://wg21.link/P2513R4>`__","LWG","``char8_t`` Compatibility and Portability Fixes","July 2022","",""
"`P2517R1 <https://wg21.link/P2517R1>`__","LWG","Add a conditional ``noexcept`` specification to ``std::apply``","July 2022","",""
"`P2520R0 <https://wg21.link/P2520R0>`__","LWG","``move_iterator`` should be a random access iterator","July 2022","",""

1 Paper # Group Paper Name Meeting Status First released version
81 `P2494R2 <https://wg21.link/P2494R2>`__ LWG Relaxing range adaptors to allow for move only types July 2022
82 `P2499R0 <https://wg21.link/P2499R0>`__ LWG ``string_view`` range constructor should be ``explicit`` July 2022 |Complete| 16.0
83 `P2502R2 <https://wg21.link/P2502R2>`__ LWG ``std::generator``: Synchronous Coroutine Generator for Ranges July 2022
84 `P2508R1 <https://wg21.link/P2508R1>`__ LWG Exposing ``std::basic-format-string`` July 2022 |Complete| 15.0
85 `P2513R4 <https://wg21.link/P2513R4>`__ LWG ``char8_t`` Compatibility and Portability Fixes July 2022
86 `P2517R1 <https://wg21.link/P2517R1>`__ LWG Add a conditional ``noexcept`` specification to ``std::apply`` July 2022
87 `P2520R0 <https://wg21.link/P2520R0>`__ LWG ``move_iterator`` should be a random access iterator July 2022

View File

@ -7,7 +7,7 @@ Number,Name,Standard,Assignee,Status,First released version
`P2418 <https://wg21.link/P2418>`__,"Add support for ``std::generator``-like types to ``std::format``","C++20",Mark de Wever,|Complete|, Clang 15
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","C++23"
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","C++23","Mark de Wever","|In Progress|"
"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|In Progress|"
"`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","C++23","Mark de Wever","|Complete|", Clang 15
"`P2585R0 <https://wg21.link/P2585R0>`__","Improving default container formatting","C++23",
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,

Can't render this file because it has a wrong number of fields in line 8.

View File

@ -460,3 +460,11 @@ As an extension these types can be used in the following headers:
* ``<format>``
* ``<random>``
Extensions to ``<format>``
--------------------------
The exposition only type ``basic-format-string`` and its typedefs
``format-string`` and ``wformat-string`` became ``basic_format_string``,
``format_string``, and ``wformat_string`` in C++23. Libc++ makes these types
available in C++20 as an extension.

View File

@ -23,16 +23,23 @@ namespace std {
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
// [format.fmt.string], class template basic-format-string
// [format.fmt.string], class template basic_format_string
template<class charT, class... Args>
struct basic-format-string; // exposition only
struct basic_format_string { // since C++23, exposition only before C++23
private:
basic_string_view<charT> str; // exposition only
public:
template<class T> consteval basic_format_string(const T& s);
constexpr basic_string_view<charT> get() const noexcept { return str; }
};
template<class... Args>
using format-string = // exposition only
basic-format-string<char, type_identity_t<Args>...>;
using format_string = // since C++23, exposition only before C++23
basic_format_string<char, type_identity_t<Args>...>;
template<class... Args>
using wformat-string = // exposition only
basic-format-string<wchar_t, type_identity_t<Args>...>;
using wformat_string = // since C++23, exposition only before C++23
basic_format_string<wchar_t, type_identity_t<Args>...>;
// [format.functions], formatting functions
template<class... Args>
@ -233,7 +240,7 @@ private:
};
// Dummy format_context only providing the parts used during constant
// validation of the basic-format-string.
// validation of the basic_format_string.
template <class _CharT>
struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
public:
@ -468,17 +475,21 @@ __vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
} // namespace __format
template <class _CharT, class... _Args>
struct _LIBCPP_TEMPLATE_VIS __basic_format_string {
basic_string_view<_CharT> __str_;
struct _LIBCPP_TEMPLATE_VIS basic_format_string {
template <class _Tp>
requires convertible_to<const _Tp&, basic_string_view<_CharT>>
consteval __basic_format_string(const _Tp& __str) : __str_{__str} {
consteval basic_format_string(const _Tp& __str) : __str_{__str} {
__format::__vformat_to(basic_format_parse_context<_CharT>{__str_, sizeof...(_Args)},
_Context{__types_.data(), __handles_.data(), sizeof...(_Args)});
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT constexpr basic_string_view<_CharT> get() const noexcept {
return __str_;
}
private:
basic_string_view<_CharT> __str_;
using _Context = __format::__compile_time_basic_format_context<_CharT>;
static constexpr array<__format::__arg_t, sizeof...(_Args)> __types_{
@ -510,11 +521,11 @@ private:
};
template <class... _Args>
using __format_string_t = __basic_format_string<char, type_identity_t<_Args>...>;
using format_string = basic_format_string<char, type_identity_t<_Args>...>;
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
using __wformat_string_t = __basic_format_string<wchar_t, type_identity_t<_Args>...>;
using wformat_string = basic_format_string<wchar_t, type_identity_t<_Args>...>;
#endif
template <class _OutIt, class _CharT, class _FormatOutIt>
@ -555,16 +566,16 @@ vformat_to(_OutIt __out_it, wstring_view __fmt, wformat_args __args) {
template <output_iterator<const char&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
format_to(_OutIt __out_it, __format_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_,
format_to(_OutIt __out_it, format_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.get(),
_VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
format_to(_OutIt __out_it, __wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.__str_,
format_to(_OutIt __out_it, wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), __fmt.get(),
_VSTD::make_wformat_args(__args...));
}
#endif
@ -586,16 +597,16 @@ vformat(wstring_view __fmt, wformat_args __args) {
#endif
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(__format_string_t<_Args...> __fmt,
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(format_string<_Args...> __fmt,
_Args&&... __args) {
return _VSTD::vformat(__fmt.__str_, _VSTD::make_format_args(__args...));
return _VSTD::vformat(__fmt.get(), _VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
format(__wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat(__fmt.__str_, _VSTD::make_wformat_args(__args...));
format(wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat(__fmt.get(), _VSTD::make_wformat_args(__args...));
}
#endif
@ -611,16 +622,16 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
template <output_iterator<const char&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __format_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_format_args(__args...));
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, format_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, __wformat_string_t<_Args...> __fmt,
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wformat_string<_Args...> __fmt,
_Args&&... __args) {
return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, __fmt.__str_, _VSTD::make_wformat_args(__args...));
return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, __fmt.get(), _VSTD::make_wformat_args(__args...));
}
#endif
@ -634,15 +645,15 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(basic_string_view<_CharT> __fmt,
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
formatted_size(__format_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)});
formatted_size(format_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(__fmt.get(), basic_format_args{_VSTD::make_format_args(__args...)});
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
formatted_size(__wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(__fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)});
formatted_size(wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(__fmt.get(), basic_format_args{_VSTD::make_wformat_args(__args...)});
}
#endif
@ -686,16 +697,16 @@ _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt v
template <output_iterator<const char&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
format_to(_OutIt __out_it, locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_,
format_to(_OutIt __out_it, locale __loc, format_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.get(),
_VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
format_to(_OutIt __out_it, locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.__str_,
format_to(_OutIt __out_it, locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt.get(),
_VSTD::make_wformat_args(__args...));
}
#endif
@ -720,17 +731,17 @@ vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string format(locale __loc,
__format_string_t<_Args...> __fmt,
format_string<_Args...> __fmt,
_Args&&... __args) {
return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_,
return _VSTD::vformat(_VSTD::move(__loc), __fmt.get(),
_VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
format(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat(_VSTD::move(__loc), __fmt.__str_,
format(locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::vformat(_VSTD::move(__loc), __fmt.get(),
_VSTD::make_wformat_args(__args...));
}
#endif
@ -748,18 +759,18 @@ _LIBCPP_HIDE_FROM_ABI format_to_n_result<_OutIt> __vformat_to_n(_OutIt __out_it,
template <output_iterator<const char&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __format_string_t<_Args...> __fmt,
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, format_string<_Args...> __fmt,
_Args&&... __args) {
return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
return _VSTD::__vformat_to_n<format_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(),
_VSTD::make_format_args(__args...));
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, __wformat_string_t<_Args...> __fmt,
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, wformat_string<_Args...> __fmt,
_Args&&... __args) {
return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.__str_,
return _VSTD::__vformat_to_n<wformat_context>(_VSTD::move(__out_it), __n, _VSTD::move(__loc), __fmt.get(),
_VSTD::make_wformat_args(__args...));
}
#endif
@ -775,15 +786,15 @@ _LIBCPP_HIDE_FROM_ABI size_t __vformatted_size(locale __loc, basic_string_view<_
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
formatted_size(locale __loc, __format_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_format_args(__args...)});
formatted_size(locale __loc, format_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.get(), basic_format_args{_VSTD::make_format_args(__args...)});
}
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
formatted_size(locale __loc, __wformat_string_t<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.__str_, basic_format_args{_VSTD::make_wformat_args(__args...)});
formatted_size(locale __loc, wformat_string<_Args...> __fmt, _Args&&... __args) {
return _VSTD::__vformatted_size(_VSTD::move(__loc), __fmt.get(), basic_format_args{_VSTD::make_wformat_args(__args...)});
}
#endif

View File

@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-format
// libc++ supports basic_format_string in C++20 as an extension
// UNSUPPORTED: !stdlib=libc++ && c++20
// <format>
// template<class charT, class... Args>
// class basic_format_string<charT, type_identity_t<Args>...>
//
// template<class T> consteval basic_format_string(const T& s);
//
// This constructor does the compile-time format string validation for the
// std::format* functions.
#include <format>
#include <string_view>
#include "test_macros.h"
void run() {
(void)std::basic_format_string<char>{"foo"};
(void)std::basic_format_string<char>{"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<char, int>{"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<char, int>{"{0:{0}}"};
(void)std::basic_format_string<char, float>{"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<char, int>{"{.3}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
(void)std::basic_format_string<wchar_t>{L"foo"};
(void)std::basic_format_string<wchar_t>{L"{}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<wchar_t, int>{L"{0:{0}P}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<wchar_t, int>{L"{0:{0}}"};
(void)std::basic_format_string<wchar_t, float>{L"{0:{0}}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
(void)std::basic_format_string<wchar_t, int>{L"{.3}"}; // expected-error-re {{call to consteval function{{.*}}is not a constant expression}}
#endif
}

View File

@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-format
// libc++ supports basic_format_string in C++20 as an extension
// UNSUPPORTED: !stdlib=libc++ && c++20
// <format>
// template<class charT, class... Args>
// class basic_format_string<charT, type_identity_t<Args>...>
//
// constexpr basic_string_view<charT> get() const noexcept { return str; }
#include <format>
#include <cassert>
#include <concepts>
#include <string_view>
#include "test_macros.h"
#include "make_string.h"
#define CSTR(S) MAKE_CSTRING(CharT, S)
#define SV(S) MAKE_STRING_VIEW(CharT, S)
template <class CharT>
constexpr bool test() {
assert((std::basic_format_string<CharT>{CSTR("foo")}.get() == SV("foo")));
assert((std::basic_format_string<CharT, int>{CSTR("{}")}.get() == SV("{}")));
assert((std::basic_format_string<CharT, int, float>{CSTR("{} {:01.23L}")}.get() == SV("{} {:01.23L}")));
// Embedded NUL character
assert((std::basic_format_string<CharT, void*, double>{SV("{}\0{}")}.get() == SV("{}\0{}")));
return true;
}
int main(int, char**) {
test<char>();
static_assert(test<char>());
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
static_assert(test<wchar_t>());
#endif
return 0;
}

View File

@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-has-no-incomplete-format
// libc++ supports basic_format_string in C++20 as an extension
// UNSUPPORTED: !stdlib=libc++ && c++20
// <format>
// template<class... Args>
// using format_string =
// basic_format_string<char, type_identity_t<Args>...>;
// template<class... Args>
// using wformat_string =
// basic_format_string<wchar_t, type_identity_t<Args>...>;
#include <format>
#include <concepts>
#include "test_macros.h"
static_assert(std::same_as<std::format_string<>, std::basic_format_string<char>>);
static_assert(std::same_as<std::format_string<int>, std::basic_format_string<char, int>>);
static_assert(std::same_as<std::format_string<int, float>, std::basic_format_string<char, int, float>>);
static_assert(std::same_as<std::format_string<int, float, void*>, std::basic_format_string<char, int, float, void*>>);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(std::same_as<std::wformat_string<>, std::basic_format_string<wchar_t>>);
static_assert(std::same_as<std::wformat_string<int>, std::basic_format_string<wchar_t, int>>);
static_assert(std::same_as<std::wformat_string<int, float>, std::basic_format_string<wchar_t, int, float>>);
static_assert(
std::same_as<std::wformat_string<int, float, void*>, std::basic_format_string<wchar_t, int, float, void*>>);
#endif

View File

@ -309,6 +309,11 @@ feature_test_macros = [ add_version_header(x) for x in [
"libcxx_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_filesystem)"
}, {
"name": "__cpp_lib_format",
# P2508, P2286, and P2419 were accepted in the same plenary and modify this
# feature-test macro. We expect to see an LWG issue soon. For now keep the
# value as is.
# TODO FMT Set P2508's feature-test macro.
#"values": { "c++20": 202106, "c++23": 202207" },
"values": { "c++20": 202106 },
"headers": ["format"],
"test_suite_guard": "!defined(_LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)",