[libcxx-commits] [libcxx] [RFC][libc++][format] Implements compile-time caching. (PR #132552)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Mar 22 07:53:32 PDT 2025
https://github.com/mordante created https://github.com/llvm/llvm-project/pull/132552
This is a proof-of-concept patch to implement caching in basic_format_string. This is only intended for --this Discourse-- discussion.
The real patch needs to do things cleaner.
lit -a -Doptimization=speed libcxx/test/benchmarks/format/parsing.bench.cpp BEFORE
| ----------------------------------------------------------------------------
| Benchmark Time CPU Iterations
| ----------------------------------------------------------------------------
| BM_empty 6.18 ns 6.17 ns 87897649
| BM_curly_open 11.2 ns 11.2 ns 62229450
| BM_curly_close 10.7 ns 10.7 ns 65368952
| BM_pipe 12.9 ns 12.9 ns 54477724
| BM_int 47.6 ns 47.6 ns 14718165
| BM_int_formatted 97.4 ns 97.4 ns 6953144
| BM_3_ints 141 ns 141 ns 4944337
| BM_3_ints_formatted 283 ns 283 ns 2464184
| BM_prefix_5_and_empty 16.5 ns 16.5 ns 42569346
| BM_prefix_5_and_curly_open 17.8 ns 17.8 ns 39273542
| BM_prefix_5_and_curly_close 17.8 ns 17.8 ns 39140130
| BM_prefix_5_and_pipe 19.5 ns 19.5 ns 35803584
| BM_prefix_5_and_int 51.1 ns 51.0 ns 13436274
| BM_prefix_5_and_int_formatted 102 ns 102 ns 6793303
| BM_prefix_5_and_3_ints 151 ns 151 ns 4573472
| BM_prefix_5_and_3_ints_formatted 291 ns 291 ns 2351850
| BM_prefix_10_and_empty 24.9 ns 24.9 ns 28109036
| BM_prefix_10_and_curly_open 27.2 ns 27.2 ns 25713490
| BM_prefix_10_and_curly_close 30.2 ns 30.2 ns 23176332
| BM_prefix_10_and_pipe 32.7 ns 32.6 ns 21420861
| BM_prefix_10_and_int 61.6 ns 61.6 ns 11034101
| BM_prefix_10_and_int_formatted 120 ns 120 ns 5610531
| BM_prefix_10_and_3_ints 156 ns 156 ns 4470738
| BM_prefix_10_and_3_ints_formatted 312 ns 311 ns 2217207
| BM_prefix_20_and_empty 52.7 ns 52.7 ns 13138425
| BM_prefix_20_and_curly_open 58.8 ns 58.8 ns 11863712
| BM_prefix_20_and_curly_close 56.6 ns 56.6 ns 12407764
| BM_prefix_20_and_pipe 56.5 ns 56.4 ns 12411003
| BM_prefix_20_and_int 86.9 ns 86.8 ns 8089463
| BM_prefix_20_and_int_formatted 153 ns 153 ns 4582443
| BM_prefix_20_and_3_ints 204 ns 204 ns 3354539
| BM_prefix_20_and_3_ints_formatted 349 ns 349 ns 2002457
| BM_prefix_40_and_empty 116 ns 116 ns 6018618
| BM_prefix_40_and_curly_open 122 ns 122 ns 5655627
| BM_prefix_40_and_curly_close 121 ns 121 ns 5722255
| BM_prefix_40_and_pipe 120 ns 120 ns 5823239
| BM_prefix_40_and_int 158 ns 158 ns 4476432
| BM_prefix_40_and_int_formatted 207 ns 207 ns 3306182
| BM_prefix_40_and_3_ints 255 ns 255 ns 2759277
| BM_prefix_40_and_3_ints_formatted 371 ns 370 ns 1884154
AFTER
| ----------------------------------------------------------------------------
| Benchmark Time CPU Iterations
| ----------------------------------------------------------------------------
| BM_empty 21.6 ns 21.6 ns 32391462
| BM_curly_open 22.9 ns 22.9 ns 30588441
| BM_curly_close 22.9 ns 22.9 ns 30592394
| BM_pipe 26.0 ns 26.0 ns 26953859
| BM_int 60.5 ns 60.5 ns 11577625
| BM_int_formatted 81.3 ns 81.2 ns 8594596
| BM_3_ints 156 ns 156 ns 4409940
| BM_3_ints_formatted 220 ns 220 ns 3187392
| BM_prefix_5_and_empty 26.0 ns 26.0 ns 26936925
| BM_prefix_5_and_curly_open 26.0 ns 26.0 ns 26965917
| BM_prefix_5_and_curly_close 26.0 ns 26.0 ns 26961356
| BM_prefix_5_and_pipe 26.0 ns 26.0 ns 26959641
| BM_prefix_5_and_int 64.1 ns 64.1 ns 10920953
| BM_prefix_5_and_int_formatted 84.4 ns 84.4 ns 8037453
| BM_prefix_5_and_3_ints 165 ns 165 ns 4217532
| BM_prefix_5_and_3_ints_formatted 247 ns 247 ns 2823553
| BM_prefix_10_and_empty 26.0 ns 26.0 ns 26949246
| BM_prefix_10_and_curly_open 26.0 ns 26.0 ns 26955524
| BM_prefix_10_and_curly_close 26.0 ns 26.0 ns 26956279
| BM_prefix_10_and_pipe 26.0 ns 26.0 ns 26947963
| BM_prefix_10_and_int 59.8 ns 59.8 ns 11674226
| BM_prefix_10_and_int_formatted 87.1 ns 87.0 ns 7814714
| BM_prefix_10_and_3_ints 165 ns 165 ns 4149378
| BM_prefix_10_and_3_ints_formatted 245 ns 245 ns 2884163
| BM_prefix_20_and_empty 29.3 ns 29.3 ns 23873593
| BM_prefix_20_and_curly_open 28.5 ns 28.4 ns 24625298
| BM_prefix_20_and_curly_close 27.5 ns 27.5 ns 25438870
| BM_prefix_20_and_pipe 29.4 ns 29.3 ns 23863557
| BM_prefix_20_and_int 57.6 ns 57.6 ns 12247413
| BM_prefix_20_and_int_formatted 104 ns 104 ns 6690343
| BM_prefix_20_and_3_ints 183 ns 183 ns 3828112
| BM_prefix_20_and_3_ints_formatted 244 ns 244 ns 2833578
| BM_prefix_40_and_empty 37.5 ns 37.5 ns 18650804
| BM_prefix_40_and_curly_open 36.8 ns 36.8 ns 19094990
| BM_prefix_40_and_curly_close 37.1 ns 37.0 ns 18904266
| BM_prefix_40_and_pipe 36.5 ns 36.5 ns 19147265
| BM_prefix_40_and_int 81.1 ns 81.0 ns 8611919
| BM_prefix_40_and_int_formatted 102 ns 102 ns 6854642
| BM_prefix_40_and_3_ints 181 ns 181 ns 3828217
| BM_prefix_40_and_3_ints_formatted 243 ns 243 ns 2814977
>From e02e5bac0bbdd2e62d8cd9d07c2f0e7810b393b9 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sat, 22 Mar 2025 15:48:57 +0100
Subject: [PATCH] [RFC][libc++][format] Implements compile-time caching.
This is a proof-of-concept patch to implement caching in
basic_format_string. This is only intended for --this Discourse--
discussion.
The real patch needs to do things cleaner.
lit -a -Doptimization=speed libcxx/test/benchmarks/format/parsing.bench.cpp
BEFORE
| ----------------------------------------------------------------------------
| Benchmark Time CPU Iterations
| ----------------------------------------------------------------------------
| BM_empty 6.18 ns 6.17 ns 87897649
| BM_curly_open 11.2 ns 11.2 ns 62229450
| BM_curly_close 10.7 ns 10.7 ns 65368952
| BM_pipe 12.9 ns 12.9 ns 54477724
| BM_int 47.6 ns 47.6 ns 14718165
| BM_int_formatted 97.4 ns 97.4 ns 6953144
| BM_3_ints 141 ns 141 ns 4944337
| BM_3_ints_formatted 283 ns 283 ns 2464184
| BM_prefix_5_and_empty 16.5 ns 16.5 ns 42569346
| BM_prefix_5_and_curly_open 17.8 ns 17.8 ns 39273542
| BM_prefix_5_and_curly_close 17.8 ns 17.8 ns 39140130
| BM_prefix_5_and_pipe 19.5 ns 19.5 ns 35803584
| BM_prefix_5_and_int 51.1 ns 51.0 ns 13436274
| BM_prefix_5_and_int_formatted 102 ns 102 ns 6793303
| BM_prefix_5_and_3_ints 151 ns 151 ns 4573472
| BM_prefix_5_and_3_ints_formatted 291 ns 291 ns 2351850
| BM_prefix_10_and_empty 24.9 ns 24.9 ns 28109036
| BM_prefix_10_and_curly_open 27.2 ns 27.2 ns 25713490
| BM_prefix_10_and_curly_close 30.2 ns 30.2 ns 23176332
| BM_prefix_10_and_pipe 32.7 ns 32.6 ns 21420861
| BM_prefix_10_and_int 61.6 ns 61.6 ns 11034101
| BM_prefix_10_and_int_formatted 120 ns 120 ns 5610531
| BM_prefix_10_and_3_ints 156 ns 156 ns 4470738
| BM_prefix_10_and_3_ints_formatted 312 ns 311 ns 2217207
| BM_prefix_20_and_empty 52.7 ns 52.7 ns 13138425
| BM_prefix_20_and_curly_open 58.8 ns 58.8 ns 11863712
| BM_prefix_20_and_curly_close 56.6 ns 56.6 ns 12407764
| BM_prefix_20_and_pipe 56.5 ns 56.4 ns 12411003
| BM_prefix_20_and_int 86.9 ns 86.8 ns 8089463
| BM_prefix_20_and_int_formatted 153 ns 153 ns 4582443
| BM_prefix_20_and_3_ints 204 ns 204 ns 3354539
| BM_prefix_20_and_3_ints_formatted 349 ns 349 ns 2002457
| BM_prefix_40_and_empty 116 ns 116 ns 6018618
| BM_prefix_40_and_curly_open 122 ns 122 ns 5655627
| BM_prefix_40_and_curly_close 121 ns 121 ns 5722255
| BM_prefix_40_and_pipe 120 ns 120 ns 5823239
| BM_prefix_40_and_int 158 ns 158 ns 4476432
| BM_prefix_40_and_int_formatted 207 ns 207 ns 3306182
| BM_prefix_40_and_3_ints 255 ns 255 ns 2759277
| BM_prefix_40_and_3_ints_formatted 371 ns 370 ns 1884154
AFTER
| ----------------------------------------------------------------------------
| Benchmark Time CPU Iterations
| ----------------------------------------------------------------------------
| BM_empty 21.6 ns 21.6 ns 32391462
| BM_curly_open 22.9 ns 22.9 ns 30588441
| BM_curly_close 22.9 ns 22.9 ns 30592394
| BM_pipe 26.0 ns 26.0 ns 26953859
| BM_int 60.5 ns 60.5 ns 11577625
| BM_int_formatted 81.3 ns 81.2 ns 8594596
| BM_3_ints 156 ns 156 ns 4409940
| BM_3_ints_formatted 220 ns 220 ns 3187392
| BM_prefix_5_and_empty 26.0 ns 26.0 ns 26936925
| BM_prefix_5_and_curly_open 26.0 ns 26.0 ns 26965917
| BM_prefix_5_and_curly_close 26.0 ns 26.0 ns 26961356
| BM_prefix_5_and_pipe 26.0 ns 26.0 ns 26959641
| BM_prefix_5_and_int 64.1 ns 64.1 ns 10920953
| BM_prefix_5_and_int_formatted 84.4 ns 84.4 ns 8037453
| BM_prefix_5_and_3_ints 165 ns 165 ns 4217532
| BM_prefix_5_and_3_ints_formatted 247 ns 247 ns 2823553
| BM_prefix_10_and_empty 26.0 ns 26.0 ns 26949246
| BM_prefix_10_and_curly_open 26.0 ns 26.0 ns 26955524
| BM_prefix_10_and_curly_close 26.0 ns 26.0 ns 26956279
| BM_prefix_10_and_pipe 26.0 ns 26.0 ns 26947963
| BM_prefix_10_and_int 59.8 ns 59.8 ns 11674226
| BM_prefix_10_and_int_formatted 87.1 ns 87.0 ns 7814714
| BM_prefix_10_and_3_ints 165 ns 165 ns 4149378
| BM_prefix_10_and_3_ints_formatted 245 ns 245 ns 2884163
| BM_prefix_20_and_empty 29.3 ns 29.3 ns 23873593
| BM_prefix_20_and_curly_open 28.5 ns 28.4 ns 24625298
| BM_prefix_20_and_curly_close 27.5 ns 27.5 ns 25438870
| BM_prefix_20_and_pipe 29.4 ns 29.3 ns 23863557
| BM_prefix_20_and_int 57.6 ns 57.6 ns 12247413
| BM_prefix_20_and_int_formatted 104 ns 104 ns 6690343
| BM_prefix_20_and_3_ints 183 ns 183 ns 3828112
| BM_prefix_20_and_3_ints_formatted 244 ns 244 ns 2833578
| BM_prefix_40_and_empty 37.5 ns 37.5 ns 18650804
| BM_prefix_40_and_curly_open 36.8 ns 36.8 ns 19094990
| BM_prefix_40_and_curly_close 37.1 ns 37.0 ns 18904266
| BM_prefix_40_and_pipe 36.5 ns 36.5 ns 19147265
| BM_prefix_40_and_int 81.1 ns 81.0 ns 8611919
| BM_prefix_40_and_int_formatted 102 ns 102 ns 6854642
| BM_prefix_40_and_3_ints 181 ns 181 ns 3828217
| BM_prefix_40_and_3_ints_formatted 243 ns 243 ns 2814977
---
libcxx/include/__format/format_functions.h | 388 +++++++++++++++++-
.../test/benchmarks/format/parsing.bench.cpp | 355 ++++++++++++++++
.../format/format.functions/cache.pass.cpp | 97 +++++
3 files changed, 835 insertions(+), 5 deletions(-)
create mode 100644 libcxx/test/benchmarks/format/parsing.bench.cpp
create mode 100644 libcxx/test/std/utilities/format/format.functions/cache.pass.cpp
diff --git a/libcxx/include/__format/format_functions.h b/libcxx/include/__format/format_functions.h
index 5feaf7e5a064a..98e2d97718831 100644
--- a/libcxx/include/__format/format_functions.h
+++ b/libcxx/include/__format/format_functions.h
@@ -76,6 +76,9 @@ template <class... _Args>
}
# endif
+template <output_iterator<const char&> _OutIt>
+_LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _OutIt vformat_to(_OutIt __out_it, string_view __fmt, format_args __args);
+
namespace __format {
/// Helper class parse and handle argument.
@@ -147,6 +150,40 @@ struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
size_t __size_;
};
+template <class _CharT>
+union __any_std_formatter {
+# define _LIBCCP_IMPLICIT_UNION(__type, __member_name) \
+ constexpr __any_std_formatter(formatter<__type, _CharT> __formatter) : __member_name(__formatter) {} \
+ explicit constexpr operator formatter<__type, _CharT>() const { return __member_name; } \
+ formatter<__type, _CharT> __member_name
+
+ _LIBCCP_IMPLICIT_UNION(bool, __b);
+ _LIBCCP_IMPLICIT_UNION(_CharT, __c);
+
+ _LIBCCP_IMPLICIT_UNION(int, __i);
+ _LIBCCP_IMPLICIT_UNION(long long, __ll);
+# if _LIBCPP_HAS_INT128
+ _LIBCCP_IMPLICIT_UNION(__int128_t, __i128);
+# endif
+
+ _LIBCCP_IMPLICIT_UNION(unsigned, __u);
+ _LIBCCP_IMPLICIT_UNION(unsigned long long, __ull);
+# if _LIBCPP_HAS_INT128
+ _LIBCCP_IMPLICIT_UNION(__uint128_t, __u128);
+# endif
+
+ _LIBCCP_IMPLICIT_UNION(float, __f);
+ _LIBCCP_IMPLICIT_UNION(double, __d);
+ _LIBCCP_IMPLICIT_UNION(long double, __ld);
+
+ _LIBCCP_IMPLICIT_UNION(const _CharT*, __cs);
+ _LIBCCP_IMPLICIT_UNION(basic_string_view<_CharT>, __sv);
+
+ _LIBCCP_IMPLICIT_UNION(const void*, __p);
+
+# undef _LIBCCP_IMPLICIT_UNION
+};
+
// [format.string.std]/8
// If { arg-idopt } is used in a width or precision, the value of the
// corresponding formatting argument is used in its place. If the
@@ -156,7 +193,7 @@ struct _LIBCPP_TEMPLATE_VIS __compile_time_basic_format_context {
//
// _HasPrecision does the formatter have a precision?
template <class _CharT, class _Tp, bool _HasPrecision = false>
-_LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_validate_argument(
+_LIBCPP_HIDE_FROM_ABI constexpr __any_std_formatter<_CharT> __compile_time_validate_argument(
basic_format_parse_context<_CharT>& __parse_ctx, __compile_time_basic_format_context<_CharT>& __ctx) {
auto __validate_type = [](__arg_t __type) {
// LWG3720 originally allowed "signed or unsigned integer types", however
@@ -185,11 +222,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_validate_argument(
if constexpr (_HasPrecision)
if (__formatter.__parser_.__precision_as_arg_)
__validate_type(__ctx.arg(__formatter.__parser_.__precision_));
+
+ return __formatter;
}
// This function is not user facing, so it can directly use the non-standard types of the "variant".
template <class _CharT>
-_LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
+_LIBCPP_HIDE_FROM_ABI constexpr __any_std_formatter<_CharT> __compile_time_visit_format_arg(
basic_format_parse_context<_CharT>& __parse_ctx,
__compile_time_basic_format_context<_CharT>& __ctx,
__arg_t __type) {
@@ -210,7 +249,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
# else
std::__throw_format_error("Invalid argument");
# endif
- return;
+ break;
case __arg_t::__unsigned:
return __format::__compile_time_validate_argument<_CharT, unsigned>(__parse_ctx, __ctx);
case __arg_t::__unsigned_long_long:
@@ -221,7 +260,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __compile_time_visit_format_arg(
# else
std::__throw_format_error("Invalid argument");
# endif
- return;
+ break;
case __arg_t::__float:
return __format::__compile_time_validate_argument<_CharT, float, true>(__parse_ctx, __ctx);
case __arg_t::__double:
@@ -368,6 +407,67 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
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)});
+
+ basic_format_parse_context<_CharT> __parse_ctx{__str_, sizeof...(_Args)};
+ _Context __ctx{__types_.data(), __handles_.data(), sizeof...(_Args)};
+
+ __state_ = [&] {
+ auto __begin = __str_.begin();
+ auto __end = __str_.end();
+ auto __element_begin = __str_.begin();
+
+ while (__begin != __end) {
+ switch (*__begin) {
+ case _CharT('{'):
+ if (__begin + 1 == __end)
+ std::__throw_format_error("The format string terminates at a '{'");
+
+ if (*(__begin + 1) == _CharT('{')) {
+ if (!__add_curly_brace(__element_begin, __begin, __element_type::__curly_brace_open))
+ return __element_state::__parsing_partial;
+
+ } else {
+ if (__element_begin != __begin) {
+ // Add the parsed string before the replaacemet
+ if (!__add_string(__element_begin, __begin))
+ return __element_state::__parsing_partial;
+ }
+
+ __format::__parse_number_result __r = __process_arg_id(__begin + 1, __end, __parse_ctx);
+ __format::__arg_t __type = __ctx.arg(__r.__value);
+ if (__type == __format::__arg_t::__none)
+ std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
+ else if (__type == __format::__arg_t::__handle)
+ //
+ // TODO ADD Handle support
+ //
+ return __element_state::__do_not_use;
+ else {
+ if (!__add_std_formatter(__element_begin, __begin, __end, __r.__value, __type, __parse_ctx, __ctx))
+ return __element_state::__parsing_partial;
+ }
+ }
+ break;
+
+ case _CharT('}'):
+ if (__begin + 1 == __end || *(__begin + 1) != _CharT('}'))
+ std::__throw_format_error("The format string contains an invalid escape sequence");
+
+ if (!__add_curly_brace(__element_begin, __begin, __element_type::__curly_brace_close))
+ return __element_state::__parsing_partial;
+ break;
+
+ default:
+ ++__begin;
+ }
+ }
+ // Handle last element. This should always be a string element, else there is a logic errror.
+ if (__element_count_ == 0 || __element_begin != __end) {
+ if (!__add_string(__element_begin, __begin))
+ return __element_state::__parsing_partial;
+ }
+ return __element_state::__parsing_completed;
+ }();
}
_LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT> get() const noexcept { return __str_; }
@@ -375,6 +475,26 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
_LIBCPP_HIDE_FROM_ABI basic_format_string(__runtime_format_string<_CharT> __s) noexcept : __str_(__s.__str_) {}
# endif
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string __vformat(format_args __args) {
+ __format::__allocating_buffer<char> __buffer;
+ switch (__state_) {
+ case __element_state::__parsing_partial:
+# ifdef _LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+ __buffer.__copy(std::string_view{"PART:"});
+# endif
+ [[fallthrough]];
+ case __element_state::__do_not_use:
+ std::vformat_to(__buffer.__make_output_iterator(), __str_, __args);
+ break;
+
+ case __element_state::__parsing_completed:
+ __format(__buffer, __args);
+ break;
+ }
+
+ return string{__buffer.__view()};
+ }
+
private:
basic_string_view<_CharT> __str_;
@@ -391,6 +511,264 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
return __handle;
}()...};
+
+ using __iterator = basic_string_view<_CharT>::iterator;
+
+ enum class __element_state {
+ // The __elements_ should not be used.
+ //
+ // This is intened to be removed once everything works
+ __do_not_use,
+
+ // The entire __str_ has been parsed and stored in __elements_
+ __parsing_completed,
+ // The input __str_ has been partially parsed and stored in __elements_.
+ //
+ // This happens when the __str_ needed more entries than available in __elemenst_,
+ // so the output can first process __elements_ and then do run-time parsing for
+ // the remaining part of __str_.
+ //
+ //
+ // TODO test autotic numbering keeps the right elements
+ //
+ __parsing_partial,
+
+ };
+
+ __element_state __state_;
+
+ enum class __element_type {
+ // This is a formatter for one of the non-handle types.
+ //
+ // All these formatters use the same state for parsing this state is stored in the array.
+ // This means at run-time there is no need to parse this replacement-field.
+ __formatter_std,
+ // A handle formatter
+ //
+ // This replacement-field is validated at compile-time, but its state is not stored.
+ // This means it needs to be parsed again at run-time.
+ __formatter_handle,
+
+ // This part of the format-string contains a wstring.
+ //
+ // This can be copied to the output verbatim.
+ __wstring,
+ // This part of the format-string contains a string with valid UTF-8.
+ //
+ // This can be copied to the output verbatim.
+ __string_valid_utf8,
+ // This part of the format-string contains a string with invalid UTF-8.
+ //
+ // Since UTF-8 is self syncing the end of the string can be determined
+ //
+ // The distiction between wstring, string (|in)valid UTF-8 is needed for
+ // std::string. When the string contains invalid Unicode the recommended
+ // practice is to diagnose this. std::print only takes `char` as input
+ // character type. So when using std::print the algorithm can use this
+ // information to diagnose things. (Diagnose means using Unicode
+ // replacement characters.)
+ __string_invalid_utf8,
+ // Found {{ in the input, unescape to {.
+ __curly_brace_open,
+ // Found }} in the input, unescape to }.
+ //
+ // Note it would be possible to use __char and then store '{' or '}' in
+ // the __parser_state_'s fill field. However that feels clumsy and there
+ // are only 2 escape characters.
+ __curly_brace_close,
+ // The field is empty.
+ //
+ // The number of fields is hard-coded so this is a sentinal value.
+ // Once an element is empty, all remaining elements are empty too.
+ __empty,
+ };
+
+ struct __element_formatter {
+ uint32_t __id = 0;
+ __format::__any_std_formatter<_CharT> __formatter;
+ };
+
+ struct __element {
+ __element_type __type;
+ union {
+ __element_formatter __formatter;
+ basic_string_view<_CharT> __string;
+ int __dummy;
+ } __data_ = {.__dummy = 0};
+ };
+
+ size_t __element_count_ = 0;
+
+ // The elements found in __str_
+ static constexpr size_t __n_elements_ = 32;
+ array<__element, __n_elements_> __elements_ = [] {
+ array<__element, __n_elements_> __result;
+ for (size_t __i = 0; __i < __result.size(); ++__i) {
+ __result[__i] = {__element_type::__empty};
+ }
+ return __result;
+ }();
+
+ // TODO add UTF-8 validation to this function.
+ [[nodiscard]] consteval bool __add_string(__iterator __begin, __iterator __end) {
+ if (__element_count_ == __elements_.size())
+ return false;
+
+ __elements_[__element_count_++] = {
+ same_as<_CharT, char> ? __element_type::__string_valid_utf8 : __element_type::__wstring,
+ {.__string = {__begin, __end}}};
+
+ return true;
+ }
+
+ // Adds an escaped curly brace, with an optional string prefix, to the parsed elements.
+ //
+ // When the input is like
+ // abc{{
+ // ^ ^ ^
+ // 1 2 3
+ //
+ // 1. input value of __element_begin
+ // 2. input value of __begin
+ // 3. output value of __element_begin and __begin
+ //
+ // pre __begin + 1 == __begin + 2 == ('{' || '}')
+ // pre __type = *(__begin + 1) == '{' ? __curly_brace_open
+ // : *(__begin + 1) == '}' ? __curly_brace_close
+ // : pre-condition failure
+ //
+ // __begin and __element_begin may point to the same element, in that case
+ // there is no parsed string. For example, "{}{{" or "{{" at the start of the
+ // input.
+ //
+ // When there is a string prefix the curly brace is stored in the string.
+ // This saves 1 entry.
+ [[nodiscard]] consteval bool
+ __add_curly_brace(__iterator& __element_begin, __iterator& __begin, __element_type __type) {
+ if (__element_begin == __begin) {
+ // No string before.
+ if (__element_count_ == __elements_.size())
+ return false;
+ __elements_[__element_count_++] = {__type};
+ } else {
+ // Merge with string.
+ if (!__add_string(__element_begin, __begin + 1))
+ return false;
+ }
+
+ __begin += 2;
+ __element_begin = __begin;
+ return true;
+ }
+
+ [[nodiscard]] consteval bool __add_std_formatter(
+ __iterator& __element_begin,
+ __iterator& __begin,
+ __iterator __end,
+ uint32_t __arg_id,
+ __format::__arg_t __type,
+ basic_format_parse_context<_CharT>& __parse_ctx,
+ _Context& __ctx) {
+ if (__element_count_ == __elements_.size())
+ return false;
+
+ __format::__any_std_formatter<_CharT> __formatter = __compile_time_visit_format_arg(__parse_ctx, __ctx, __type);
+ __begin = __parse_ctx.begin();
+ if (__begin == __end || *__begin != _CharT('}'))
+ std::__throw_format_error("The replacement field misses a terminating '}'");
+
+ ++__begin;
+ __element_begin = __begin;
+
+ __elements_[__element_count_++] = {__element_type::__formatter_std, {.__formatter{__arg_id, __formatter}}
+
+ };
+
+ return true;
+ }
+
+ [[nodiscard]] consteval __format::__parse_number_result<__iterator>
+ __process_arg_id(__iterator __begin, __iterator __end, basic_format_parse_context<_CharT>& __parse_ctx) {
+ __format::__parse_number_result __result = __format::__parse_arg_id(__begin, __end, __parse_ctx);
+ if (__result.__last == __end)
+ std::__throw_format_error("The argument index should end with a ':' or a '}'");
+
+ //
+ // Note we always need to parse to get the proper default parser settings,
+ // this differs from where we copy pasted the data
+ //
+ // bool __parse = *__result.__last == _CharT(':');
+ switch (*__result.__last) {
+ case _CharT(':'):
+ // The arg-id has a format-specifier, advance the input to the format-spec.
+ __parse_ctx.advance_to(__result.__last + 1);
+ break;
+ case _CharT('}'):
+ // The arg-id has no format-specifier.
+ __parse_ctx.advance_to(__result.__last);
+ break;
+ default:
+ std::__throw_format_error("The argument index should end with a ':' or a '}'");
+ }
+
+ return __result;
+ }
+
+ void __format(__format::__allocating_buffer<char>& __buffer, format_args __args) {
+# ifdef _LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+ __buffer.__copy(std::string_view{"FULL:"});
+# endif
+ auto __ctx = std::__format_context_create(__buffer.__make_output_iterator(), __args);
+
+ for (auto& __element : __elements_) {
+ if (__element.__type == __element_type::__empty)
+ break;
+
+ switch (__element.__type) {
+ case __element_type::__formatter_std:
+ {
+ std::__visit_format_arg(
+ [&](auto __arg) {
+ if constexpr (same_as<decltype(__arg), monostate>)
+ std::__throw_format_error("The argument index value is too large for the number of arguments supplied");
+ else if constexpr (same_as<decltype(__arg), typename basic_format_arg<decltype(__ctx)>::handle>)
+ std::__throw_format_error("Not implemented");
+ else {
+ // TODO __formatter.__formatter looks odd, maybe the first should
+ // be __replacement_field.
+ const auto& __formatter =
+ static_cast<formatter<decltype(__arg), _CharT>>(__element.__data_.__formatter.__formatter);
+
+ __ctx.advance_to(__formatter.format(__arg, __ctx));
+ }
+ },
+ __ctx.arg(__element.__data_.__formatter.__id));
+
+ } break;
+
+ case __element_type::__formatter_handle:
+ break;
+ case __element_type::__string_invalid_utf8:
+ if (false) {
+ break;
+ }
+ [[fallthrough]];
+ case __element_type::__string_valid_utf8:
+ [[fallthrough]];
+ case __element_type::__wstring:
+ __buffer.__copy(__element.__data_.__string);
+ break;
+ case __element_type::__curly_brace_open:
+ __buffer.push_back(_CharT('{'));
+ break;
+ case __element_type::__curly_brace_close:
+ __buffer.push_back(_CharT('}'));
+ break;
+ case __element_type::__empty:
+ break;
+ }
+ }
+ }
};
template <class... _Args>
@@ -471,7 +849,7 @@ vformat(wstring_view __fmt, wformat_args __args) {
template <class... _Args>
[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI string
format(format_string<_Args...> __fmt, _Args&&... __args) {
- return std::vformat(__fmt.get(), std::make_format_args(__args...));
+ return __fmt.__vformat(std::make_format_args(__args...));
}
# if _LIBCPP_HAS_WIDE_CHARACTERS
diff --git a/libcxx/test/benchmarks/format/parsing.bench.cpp b/libcxx/test/benchmarks/format/parsing.bench.cpp
new file mode 100644
index 0000000000000..c31778efcc8c0
--- /dev/null
+++ b/libcxx/test/benchmarks/format/parsing.bench.cpp
@@ -0,0 +1,355 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// This benchmark does formatting but the main goal is to test the parsing.
+// Calling std::format without formatting args may me somewhat unrealistc,
+// however it is realistic for std::print. Unfortunately std::print is hard
+// to benchmark since the writing to the terminal is not what is inteded to
+// be measured.
+
+#include <cstdio>
+#include <format>
+#include <array>
+#include <string>
+#include <string_view>
+
+#include "benchmark/benchmark.h"
+
+// No prefix
+
+static void BM_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_empty);
+
+static void BM_curly_open(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("{{");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_curly_open);
+
+static void BM_curly_close(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("}}");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_curly_close);
+
+static void BM_pipe(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("||");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_pipe);
+
+static void BM_int(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("{}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_int);
+
+static void BM_int_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_int_formatted);
+
+static void BM_3_ints(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("{},{},{}", 42, 0, 99);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_3_ints);
+
+static void BM_3_ints_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_3_ints_formatted);
+
+// Prefix 5
+
+static void BM_prefix_5_and_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_empty);
+
+static void BM_prefix_5_and_curly_open(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'{{");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_curly_open);
+
+static void BM_prefix_5_and_curly_close(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'}}");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_curly_close);
+
+static void BM_prefix_5_and_pipe(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'||");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_pipe);
+
+static void BM_prefix_5_and_int(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'{}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_int);
+
+static void BM_prefix_5_and_int_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_int_formatted);
+
+static void BM_prefix_5_and_3_ints(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'{},{},{}", 42, 0, 99);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_3_ints);
+
+static void BM_prefix_5_and_3_ints_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_5_and_3_ints_formatted);
+
+// Prefix 10
+
+static void BM_prefix_10_and_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_empty);
+
+static void BM_prefix_10_and_curly_open(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'{{");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_curly_open);
+
+static void BM_prefix_10_and_curly_close(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'}}");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_curly_close);
+
+static void BM_prefix_10_and_pipe(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'||");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_pipe);
+
+static void BM_prefix_10_and_int(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'{}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_int);
+
+static void BM_prefix_10_and_int_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_int_formatted);
+
+static void BM_prefix_10_and_3_ints(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'{},{},{}", 42, 0, 99);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_3_ints);
+
+static void BM_prefix_10_and_3_ints_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_10_and_3_ints_formatted);
+
+// Prefix 20
+
+static void BM_prefix_20_and_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_empty);
+
+static void BM_prefix_20_and_curly_open(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'{{");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_curly_open);
+
+static void BM_prefix_20_and_curly_close(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'}}");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_curly_close);
+
+static void BM_prefix_20_and_pipe(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'||");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_pipe);
+
+static void BM_prefix_20_and_int(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'{}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_int);
+
+static void BM_prefix_20_and_int_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_int_formatted);
+
+static void BM_prefix_20_and_3_ints(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'{},{},{}", 42, 0, 99);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_3_ints);
+
+static void BM_prefix_20_and_3_ints_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_20_and_3_ints_formatted);
+
+// Prefix 40
+
+static void BM_prefix_40_and_empty(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_empty);
+
+static void BM_prefix_40_and_curly_open(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{{");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_curly_open);
+
+static void BM_prefix_40_and_curly_close(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'}}");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_curly_close);
+
+static void BM_prefix_40_and_pipe(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'||");
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_pipe);
+
+static void BM_prefix_40_and_int(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_int);
+
+static void BM_prefix_40_and_int_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_int_formatted);
+
+static void BM_prefix_40_and_3_ints(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{},{},{}", 42, 0, 99);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_3_ints);
+
+static void BM_prefix_40_and_3_ints_formatted(benchmark::State& state) {
+ for (auto _ : state) {
+ auto s = std::format("aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'aaaa'{0:-^#6x},{0:-^#6x},{0:-^#6x}", 42);
+ benchmark::DoNotOptimize(s);
+ }
+}
+BENCHMARK(BM_prefix_40_and_3_ints_formatted);
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp b/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp
new file mode 100644
index 0000000000000..a1d8ced4ae289
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/cache.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+// 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: GCC-ALWAYS_INLINE-FIXME
+
+
+// TODO FMT This test should not require std::to_chars(floating-point)
+// XFAIL: availability-fp_to_chars-missing
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEBUG_BASIC_FORMAT_STRING_CACHE
+
+// <format>
+
+
+// This is a test for the new caching mechanism.
+
+#include <format>
+#include <cassert>
+#include <vector>
+
+#include "make_string.h"
+#include "test_format_string.h"
+
+#include <iostream>
+#include <type_traits>
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+template < class CharT, class... Args>
+void check(std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
+ std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
+#ifndef TEST_HAS_NO_LOCALIZATION
+ if constexpr (std::same_as<CharT, char>)
+ if (out != expected)
+ std::cerr << "\nFormat string " << fmt.get() << "\nExpected output " << expected << "\nActual output " << out
+ << '\n';
+#endif
+ assert(out == expected);
+};
+
+template <class CharT>
+struct String {
+ std::basic_string<CharT> s;
+};
+
+template <class CharT>
+struct std::formatter<String<CharT>, CharT> : std::formatter<std::basic_string_view<CharT>> {
+ template <class FormatContext>
+ typename FormatContext::iterator format(const String<CharT>& str, FormatContext& ctx) const {
+ return std::formatter<std::basic_string_view<CharT>>::format(std::basic_string_view<CharT >{str.s}, ctx);
+ }
+};
+
+template <class CharT>
+static void test() {
+ check(SV("FULL:{"), SV("{{"));
+ check(SV("FULL:foo{"), SV("foo{{"));
+ check(SV("FULL:foo{{"), SV("foo{{{{"));
+
+ check(SV("FULL:}"), SV("}}"));
+ check(SV("FULL:foo}"), SV("foo}}"));
+ check(SV("FULL:foo}}"), SV("foo}}}}"));
+
+ check(SV("FULL:foo{}"), SV("foo{{}}"));
+ check(SV("FULL:foo{{}}"), SV("foo{{{{}}}}"));
+ check(SV("FULL:foo}{"), SV("foo}}{{"));
+ check(SV("FULL:foo}}{{"), SV("foo}}}}{{{{"));
+
+ check(SV("FULL:ZZZ"), SV("ZZZ"));
+ check(SV("FULL:Z{Z"), SV("Z{{Z"));
+ check(SV("FULL:Z}Z"), SV("Z}}Z"));
+ check(SV("FULL:Z{Z{"), SV("Z{{Z{{"));
+ check(SV("FULL:Z}Z{Z"), SV("Z}}Z{{Z"));
+
+ check(SV("FULL:ZZZ"), SV("{}"), SV("ZZZ"));
+ check(SV("FULL:42"), SV("{}"), 42);
+ check(SV("FULL:true"), SV("{}"), true);
+
+ check(SV("FULL:ZZZ"), SV("{:}"), SV("ZZZ"));
+ check(SV("FULL:0x0042"), SV("{:#06x}"), 0x42);
+ check(SV("FULL:0x0042=answer"), SV("{:#06x}={}"), 0x42, SV("answer"));
+
+// check(SV("FULL:hello world"), SV("{} world"), String<char>{"hello"});
+// check(SV("FULL:hello world"), SV("{0:} world"), String<char>{"hello"});
+
+ // TODO TEST WITH ARG EATER
+}
+
+int main(int, char**) {
+ test<char>();
+
+ return 0;
+}
More information about the libcxx-commits
mailing list