diff --git a/libcxx/benchmarks/CMakeLists.txt b/libcxx/benchmarks/CMakeLists.txt --- a/libcxx/benchmarks/CMakeLists.txt +++ b/libcxx/benchmarks/CMakeLists.txt @@ -142,8 +142,8 @@ RUNTIME_OUTPUT_DIRECTORY "${BENCHMARK_OUTPUT_DIR}" COMPILE_FLAGS "${BENCHMARK_TEST_LIBCXX_COMPILE_FLAGS}" LINK_FLAGS "${BENCHMARK_TEST_LIBCXX_LINK_FLAGS}" - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED YES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED NO CXX_EXTENSIONS NO) cxx_link_system_libraries(${libcxx_target}) if (LIBCXX_BENCHMARK_NATIVE_STDLIB) @@ -172,8 +172,8 @@ INCLUDE_DIRECTORIES "" COMPILE_FLAGS "${BENCHMARK_TEST_NATIVE_COMPILE_FLAGS}" LINK_FLAGS "${BENCHMARK_TEST_NATIVE_LINK_FLAGS}" - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED YES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED NO CXX_EXTENSIONS NO) endif() endfunction() diff --git a/libcxx/benchmarks/vformat_to.bench.cpp b/libcxx/benchmarks/vformat_to.bench.cpp new file mode 100644 --- /dev/null +++ b/libcxx/benchmarks/vformat_to.bench.cpp @@ -0,0 +1,258 @@ +//===----------------------------------------------------------------------===// +// 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 +// +//===----------------------------------------------------------------------===// + +// TODO FMT Remove once libc++ requires C++20 support +#if _LIBCPP_STD_VER > 17 + +#include + +#include "benchmark/benchmark.h" +#include "test_macros.h" +#include "make_string.h" + +#ifdef NDEBUG +// Undefine the macro to validate whether the benchmarks behave as expected. +//#undef NDEBUG +#endif +#include + +// Benchmarks to test std::vformat_to's speed. +// +// The typical use of the format library will be by using std::format, which +// will use a std::back_insert_iterator. This gives overhead due to validating +// the output buffer's size and, if necessary, increase the size of the output +// buffer. Since the benchmark tries to benchmark std::format's efficiency this +// overhead will influence the test. Therefore the benchmarks use a +// pre-allocated buffer. +// +// The non-templated function std::vformat_to is a wrapper that calls the +// templated function std::__format::__vformat_to. Since the benchmarks test +// different character types it uses std::__format::__vformat_to instead of +// std::vformat_to. +// +// The std::format function will extend the life-time of all its arguments +// during the evaluation of std::format. When calling a 'v-function' like +// std::vformat, the caller has to make sure the std::string arguments' +// lifetime will be extended until the std::vformat is done. +// To avoid recreating the fmt argument during the benchmark it's created +// before the benchmark. This means the lifetime of the data of fmt needs to +// be extended. + +// Test writing a string with no formatting specifiers. This is basically a +// slow string copy. +template +static void BM_VFormatToEmptyArgs(benchmark::State& state) { + std::basic_string out(state.range(0), CharT(0)); + std::basic_string f(state.range(0), CharT(' ')); + std::basic_string_view fmt{f}; + auto store = std::make_format_args< + std::basic_format_context >(); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(out == f); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToEmptyArgs, char) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToEmptyArgs, wchar_t) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Tests the escaping of {{ to {. +template +static void BM_VFormatToLBrace(benchmark::State& state) { + std::basic_string out(state.range(0), CharT(0)); + std::basic_string f(state.range(0) * 2, CharT('{')); + std::basic_string_view fmt{f}; + auto store = std::make_format_args< + std::basic_format_context >(); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT('{'))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToLBrace, char) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToLBrace, wchar_t) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Tests the escaping of }} to }. +template +static void BM_VFormatToRBrace(benchmark::State& state) { + std::basic_string out(state.range(0), CharT(0)); + std::basic_string f(state.range(0) * 2, CharT('}')); + std::basic_string_view fmt{f}; + auto store = std::make_format_args< + std::basic_format_context >(); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT('}'))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToRBrace, char) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToRBrace, wchar_t) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Test writing a single argument with a variable size string. +template +static void BM_VFormatToSingleArg(benchmark::State& state) { + std::basic_string out(state.range(0), CharT(0)); + std::basic_string f = MAKE_STRING(CharT, "{}"); + std::basic_string_view fmt{f}; + auto data = std::basic_string(state.range(0), CharT(' ')); + auto store = std::make_format_args< + std::basic_format_context >(data); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT(' '))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToSingleArg, char) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToSingleArg, wchar_t) + ->Arg(0) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Test writing multiple arguments. All arguments are a 16 character string. +template +static void BM_VFormatToMultipleStringArgs(benchmark::State& state) { + std::basic_string out(state.range(0) * 16, CharT(0)); + std::basic_string f; + for (int i = 0; i < state.range(0); ++i) + f += MAKE_STRING(CharT, "{0}"); + std::basic_string_view fmt{f}; + auto data = std::basic_string(16, CharT(' ')); + auto store = std::make_format_args< + std::basic_format_context >(data); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT(' '))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToMultipleStringArgs, char) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToMultipleStringArgs, wchar_t) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Test writing multiple arguments. All arguments are a Boolean value. +template +static void BM_VFormatToMultipleBoolArgs(benchmark::State& state) { + std::basic_string out(state.range(0), CharT(0)); + std::basic_string f; + for (int i = 0; i < state.range(0); ++i) + f += MAKE_STRING(CharT, "{0}"); + std::basic_string_view fmt{f}; + auto store = std::make_format_args< + std::basic_format_context >(true); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT('1'))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToMultipleBoolArgs, char) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToMultipleBoolArgs, wchar_t) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// Test writing multiple arguments. +// All arguments are set of a 16 character string and a Boolean value. +template +static void BM_VFormatToMultipleStringBoolArgs(benchmark::State& state) { + std::basic_string out(state.range(0) * 17, CharT(0)); + std::basic_string f; + for (int i = 0; i < state.range(0); ++i) + f += MAKE_STRING(CharT, "{0}{1}"); + std::basic_string_view fmt{f}; + auto data = std::basic_string(16, CharT(' ')); + auto store = std::make_format_args< + std::basic_format_context >(data, true); + std::basic_format_args args{store}; + + std::__format::__vformat_to(out.begin(), fmt, args); + assert(size_t(std::count(out.begin(), out.end(), CharT(' ')) + + std::count(out.begin(), out.end(), CharT('1'))) == out.size()); + + for (auto _ : state) + benchmark::DoNotOptimize( + std::__format::__vformat_to(out.begin(), fmt, args)); + + state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(out.size())); +} +BENCHMARK_TEMPLATE(BM_VFormatToMultipleStringBoolArgs, char) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); +BENCHMARK_TEMPLATE(BM_VFormatToMultipleStringBoolArgs, wchar_t) + ->RangeMultiplier(4) + ->Range(1, 1 << 20); + +// TODO FMT Test with other argument types + +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) + return 1; + + benchmark::RunSpecifiedBenchmarks(); +} +#else +int main(int, char**) { return 0; } +#endif diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -38,7 +38,10 @@ New Features ------------ -- ... +- There's initial support for the C++20 header ````. The implementation + is incomplete. Some functions are known to be inefficient; both in memory + usage and performance. The implementation is considered experimental and isn't + considered ABI stable. API Changes ----------- diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -54,6 +54,58 @@ template using format_args_t = basic_format_args>; + // [format.functions], formatting functions + template + string format(string_view fmt, const Args&... args); + template + wstring format(wstring_view fmt, const Args&... args); + + string vformat(string_view fmt, format_args args); + wstring vformat(wstring_view fmt, wformat_args args); + + template + Out format_to(Out out, string_view fmt, const Args&... args); + template + Out format_to(Out out, wstring_view fmt, const Args&... args); + + template + Out vformat_to(Out out, string_view fmt, + format_args_t, char> args); + template + Out vformat_to(Out out, wstring_view fmt, + format_args_t, wchar_t> args); + + template struct format_to_n_result { + Out out; + iter_difference_t size; + }; + + template + format_to_n_result format_to_n(Out out, iter_difference_t n, + string_view fmt, const Args&... args); + template + format_to_n_result format_to_n(Out out, iter_difference_t n, + wstring_view fmt, const Args&... args); + + template + size_t formatted_size(string_view fmt, const Args&... args); + template + size_t formatted_size(wstring_view fmt, const Args&... args); + + + // [format.formatter], formatter + template<> struct formatter; + template<> struct formatter; + template<> struct formatter; + + template<> struct formatter; + template<> struct formatter; + template struct formatter; + template + struct formatter, charT>; + template + struct formatter, charT>; + // [format.parse.ctx], class template basic_format_parse_context template class basic_format_parse_context { @@ -127,7 +179,6 @@ explicit operator bool() const noexcept; }; - template see below visit_format_arg(Visitor&& vis, basic_format_arg arg); @@ -187,6 +238,7 @@ */ #include <__config> +#include <__debug> #include #include #include @@ -261,6 +313,19 @@ (_VSTD::__has_iterator_category_convertible_to< _Tp, _VSTD::forward_iterator_tag>::value && !_VSTD::__is_exactly_cpp17_input_iterator<_Tp>::value); +namespace __format { +template +using __iter_difference_t = + typename _VSTD::iterator_traits<_It>::difference_type; +} // namespace __format + +// The std::back_insert_iterator seems to return a void for the +// difference_type. This sound like a bug, however since the code should use +// the concept don't fix it. Instead use work-around to turn a void into a long. +template +using __iter_difference_t = + _VSTD::conditional_t<_VSTD::is_void_v<__format::__iter_difference_t<_It> >, + long, __format::__iter_difference_t<_It> >; // Forward declarations. @@ -339,6 +404,89 @@ __throw_format_error("Argument index outside the valid range"); } + /** + * Advances the input iterator to the next element. + * + * @pre The next input element is valid (__begin_ + 1 != __end_) + * + * @returns The next input element. + */ + _LIBCPP_HIDDEN constexpr _CharT __consume() { + _LIBCPP_ASSERT( + __begin_ != __end_, + "The caller should ensure that there's still input to parse"); + + ++__begin_; + if (__begin_ == __end_) + __throw_format_error("Unterminated format string"); + return *__begin_; + } + + /** Advances the input iterator to the next element. */ + _LIBCPP_HIDDEN constexpr void __advance() noexcept { + _LIBCPP_ASSERT( + __begin_ != __end_, + "The caller should ensure that there's still input to parse"); + + ++__begin_; + } + + /** + * Gets the arg-id for a format argument. + * + * If the arg-id is omitted it returns the next arg-id, else returns the + * arg-id specified in the input. The input iterator points at the first + * character of the optional arg-id. + * + * @pre The input iterator points at a valid optional arg-id. + * + * @note The function doesn't validate whether the character after the + * optional arg-id is valid. This means '0a' is considered a valid arg-id + * containing 0, but 'a' is considered invalid. + */ + [[nodiscard]] _LIBCPP_HIDDEN constexpr size_t __get_arg_id() { + _LIBCPP_ASSERT( + __begin_ != __end_, + "The caller should ensure that there's still input to parse"); + + switch (*__begin_) { + case _CharT('0'): + check_arg_id(0); + __consume(); + return 0; + + case _CharT(':'): + case _CharT('}'): + return next_arg_id(); + + case _CharT('1'): + case _CharT('2'): + case _CharT('3'): + case _CharT('4'): + case _CharT('5'): + case _CharT('6'): + case _CharT('7'): + case _CharT('8'): + case _CharT('9'): + break; + + default: + __throw_format_error("Found no arg-id, format-specifier, or '}'"); + } + + size_t __id = *__begin_ - _CharT('0'); + while (true) { + _CharT __c = __consume(); + if ((__c < _CharT('0')) || (__c > _CharT('9'))) + break; + + __id *= 10; + __id += __c - _CharT('0'); + } + check_arg_id(__id); + return __id; + } + private: iterator __begin_; iterator __end_; @@ -447,7 +595,7 @@ template _LIBCPP_INLINE_VISIBILITY explicit basic_format_arg( const _Tp& __value) noexcept - // Avoid ambiguous lookups with types where sizeof(_Tp) <= sizeof(int) + // Avoid ambiguous lookups with types where sizeof(_Tp) <= sizeof(int) // Since sizeof(char_type) is not larger than sizeof(int) don't test them. requires(_VSTD::signed_integral<_Tp> && sizeof(_Tp) > sizeof(int) && sizeof(_Tp) <= sizeof(long long)) { @@ -476,7 +624,7 @@ _LIBCPP_INLINE_VISIBILITY explicit basic_format_arg(const char_type* __s) { if (!__s) - __throw_format_error("Used a nullptr argument to initialize a C-string."); + __throw_format_error("Used a nullptr argument to initialize a C-string"); __value_ = __s; } @@ -528,8 +676,10 @@ return __id < __size_ ? __data_[__id] : basic_format_arg<_Context>(); } - // Returns: The number of formatting arguments. - size_t __size() const noexcept { return __size_; } + /** @returns The number of formatting arguments. */ + [[nodiscard]] _LIBCPP_HIDDEN size_t __size() const noexcept { + return __size_; + } private: size_t __size_{0}; @@ -576,6 +726,10 @@ _LIBCPP_INLINE_VISIBILITY iterator out() { return __out_it_; } _LIBCPP_INLINE_VISIBILITY void advance_to(iterator __it) { __out_it_ = __it; } + // Writes __c to the output. Advances the output iterator to the next + // element. + void __write(_CharT __c) { *__out_it_++ = __c; } + private: iterator __out_it_; basic_format_args __args_; @@ -627,6 +781,479 @@ return __make_format_args(__args...); } +namespace __format { +template +struct _LIBCPP_TEMPLATE_VIS __formatter_char { + _LIBCPP_INLINE_VISIBILITY + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_INLINE_VISIBILITY + auto format(_Tp __c, auto& __ctx) -> decltype(__ctx.out()) { + // TODO FMT Implement the parsed formatting arguments. + auto __out_it = __ctx.out(); + *__out_it++ = _CharT(__c); + return __out_it; + } +}; + +template +struct _LIBCPP_TEMPLATE_VIS __formatter_c_string { + _LIBCPP_INLINE_VISIBILITY + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_INLINE_VISIBILITY + auto format(const _CharT* __str, auto& __ctx) -> decltype(__ctx.out()) { + // TODO FMT Implement the parsed formatting arguments. + auto __out_it = __ctx.out(); + while (*__str) + *__out_it++ = *__str++; + return __out_it; + } +}; + +template +struct _LIBCPP_TEMPLATE_VIS __formatter_string { + _LIBCPP_INLINE_VISIBILITY + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_INLINE_VISIBILITY + auto format(_VSTD::basic_string_view<_CharT> __str, auto& __ctx) + -> decltype(__ctx.out()) { + // TODO FMT Implement the parsed formatting arguments. + auto __out_it = __ctx.out(); + for (const auto __c : __str) + *__out_it++ = __c; + return __out_it; + } +}; + +template +requires(_VSTD::is_arithmetic_v<_Tp> && + !_VSTD::same_as<_Tp, bool>) struct _LIBCPP_INLINE_VISIBILITY + __formatter_arithmetic { + _LIBCPP_INLINE_VISIBILITY + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement + return __parse_ctx.begin(); + } + + _LIBCPP_INLINE_VISIBILITY + auto format(_Tp __value, auto& __ctx) -> decltype(__ctx.out()) { + return __handle_format(__value, __ctx); + } + +private: + template + _LIBCPP_HIDDEN static _VSTD::string __convert(_Uv __value) + requires(_VSTD::same_as<_CharT, char>) { + return _VSTD::to_string(__value); + } + template + _LIBCPP_HIDDEN static _VSTD::wstring __convert(_Uv __value) + requires(_VSTD::same_as<_CharT, wchar_t>) { + return _VSTD::to_wstring(__value); + } + + template + _LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx) + -> decltype(__ctx.out()) +#ifndef _LIBCPP_HAS_NO_INT128 + requires(!_VSTD::same_as<_Uv, __int128_t> && + !_VSTD::same_as<_Uv, __uint128_t>) +#endif + { + // TODO FMT Implement using formatting arguments + // TODO FMT Improve PoC since using std::to_string is inefficient. + // Note the code doesn't use std::string::iterator since the unit tests + // test with debug iterators and they fail with strings created from + // std::to_string. + auto __str = __convert(__value); + auto __out_it = __ctx.out(); + for (size_t __i = 0, __e = __str.size(); __i != __e; ++__i) + *__out_it++ = __str[__i]; + return __out_it; + } +#ifndef _LIBCPP_HAS_NO_INT128 + template + _LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx) + -> decltype(__ctx.out()) requires(_VSTD::same_as<_Uv, __int128_t> || + _VSTD::same_as<_Uv, __uint128_t>) { + using _To = _VSTD::conditional_t<_VSTD::is_signed_v<_Uv>, long long, + unsigned long long>; + // TODO FMT Implement full 128 bit support. + if (__value < _VSTD::numeric_limits<_To>::min() || + __value > _VSTD::numeric_limits<_To>::max()) + __throw_format_error("128 bit value is outside of implemented range"); + + return __handle_format(static_cast<_To>(__value), __ctx); + } +#endif +}; +} // namespace __format + +// Currently not implemented specializations throw an exception when used. This +// is not conform the Standard. However not all Standard defined formatters +// have been implemented yet. Until that time the current behavior is intended. +// TODO FMT Disable the default specialization. +template +struct _LIBCPP_TEMPLATE_VIS formatter { + _LIBCPP_NORETURN _LIBCPP_INLINE_VISIBILITY auto parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + __throw(); + } + + _LIBCPP_NORETURN _LIBCPP_INLINE_VISIBILITY auto format(_Tp, auto& __ctx) + -> decltype(__ctx.out()) { + __throw(); + } + +private: + _LIBCPP_NORETURN _LIBCPP_HIDDEN void __throw() { + __throw_format_error("Argument type not implemented yet"); + } +}; + +// Using this specialization means using an invalid argument. +// TODO FMT This should probably be removed and throw a generic exception. +template +struct _LIBCPP_TEMPLATE_VIS formatter<_VSTD::monostate, _CharT> { + _LIBCPP_NORETURN _LIBCPP_INLINE_VISIBILITY auto parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + __throw(); + } + + _LIBCPP_NORETURN _LIBCPP_INLINE_VISIBILITY auto format(_VSTD::monostate, + auto& __ctx) + -> decltype(__ctx.out()) { + __throw(); + } + +private: + _LIBCPP_NORETURN _LIBCPP_HIDDEN void __throw() { + __throw_format_error("Argument index out of bounds"); + } +}; + +// [format.formatter.spec]/2.1 The specializations + +template <> +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_char {}; + +template <> +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_char {}; + +template <> +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_char {}; + +// [format.formatter.spec]/2.2 For each charT, the string type specializations + +template +struct _LIBCPP_TEMPLATE_VIS formatter<_CharT*, _CharT> + : public __format::__formatter_c_string<_CharT> { + using _Base = __format::__formatter_c_string<_CharT>; + + _LIBCPP_INLINE_VISIBILITY auto format(_CharT* __str, auto& __ctx) + -> decltype(__ctx.out()) { + _LIBCPP_ASSERT(__str, "The basic_format_arg constructor should have " + "prevented an invalid pointer"); + return _Base::format(__str, __ctx); + } +}; + +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_c_string<_CharT> {}; + +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_string<_CharT> { + using _Base = __format::__formatter_string<_CharT>; + + _LIBCPP_INLINE_VISIBILITY auto format(const _CharT __str[_Size], auto& __ctx) + -> decltype(__ctx.out()) { + return _Base::format(_VSTD::basic_string_view<_CharT>(__str, _Size), __ctx); + } +}; + +template +struct _LIBCPP_TEMPLATE_VIS + formatter<_VSTD::basic_string<_CharT, _Traits, _Allocator>, _CharT> + : public __format::__formatter_string<_CharT> { + using _Base = __format::__formatter_string<_CharT>; + + _LIBCPP_INLINE_VISIBILITY auto + format(const _VSTD::basic_string<_CharT, _Traits, _Allocator>& __str, + auto& __ctx) -> decltype(__ctx.out()) { + return _Base::format(_VSTD::basic_string_view<_CharT>(__str), __ctx); + } +}; + +template +struct _LIBCPP_TEMPLATE_VIS + formatter<_VSTD::basic_string_view<_CharT, _Traits>, _CharT> + : public __format::__formatter_string<_CharT> {}; + +// [format.formatter.spec]/2.3 +// For each charT, for each cv-unqualified arithmetic type ArithmeticT other +// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization + +// Boolean. +template +struct _LIBCPP_TEMPLATE_VIS formatter { + _LIBCPP_INLINE_VISIBILITY + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement + return __parse_ctx.begin(); + } + + _LIBCPP_INLINE_VISIBILITY + auto format(bool __b, auto& __ctx) -> decltype(__ctx.out()) { + // TODO FMT Implement using formatting arguments + auto __out_it = __ctx.out(); + *__out_it++ = _CharT('0') + __b; + return __out_it; + } +}; + +// Signed integral types. +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +#ifndef _LIBCPP_HAS_NO_INT128 +template +struct _LIBCPP_TEMPLATE_VIS formatter<__int128_t, _CharT> + : public __format::__formatter_arithmetic<__int128_t, _CharT> {}; +#endif + +// Unsigned integral types. +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +#ifndef _LIBCPP_HAS_NO_INT128 +template +struct _LIBCPP_TEMPLATE_VIS formatter<__uint128_t, _CharT> + : public __format::__formatter_arithmetic<__uint128_t, _CharT> {}; +#endif + +// Floating point types. +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS formatter + : public __format::__formatter_arithmetic {}; + +namespace __format { +template +_LIBCPP_HIDDEN void __handle_replacement_string(_ParseCtx& __parse_ctx, + _Ctx& __ctx) { + using _CharT = typename _ParseCtx::char_type; + static_assert(_VSTD::is_same_v); + + size_t __id = __parse_ctx.__get_arg_id(); + switch (*__parse_ctx.begin()) { + case _CharT(':'): + // The arg-id has no format-specifier, advance the input to the format-spec. + __parse_ctx.__consume(); + break; + case _CharT('}'): + // The arg-id has no format-specifier. + break; + default: + __throw_format_error("Expected ':' or '}' after an arg-id"); + } + + _VSTD::visit_format_arg( + [&](auto&& __arg) { + formatter<_VSTD::decay_t, _CharT> __formatter; + __parse_ctx.advance_to(__formatter.parse(__parse_ctx)); + __ctx.advance_to(__formatter.format(__arg, __ctx)); + }, + __ctx.arg(__id)); + + _LIBCPP_ASSERT(__parse_ctx.begin() != __parse_ctx.end(), + "Sanity check before deferring the iterator"); + _LIBCPP_ASSERT(*__parse_ctx.begin() == _CharT('}'), + "The replacement-field processing should stop at a '}'"); + + __parse_ctx.__advance(); +} + +template +_LIBCPP_INLINE_VISIBILITY _OutIt +__vformat_to(_OutIt __out_it, _VSTD::basic_string_view<_CharT> __fmt, + format_args_t<_VSTD::type_identity_t<_OutIt>, _CharT> __args) { + basic_format_parse_context __parse_ctx{__fmt, __args.__size()}; + basic_format_context __ctx{__out_it, __args}; + + while (__parse_ctx.begin() != __parse_ctx.end()) { + _CharT __c = *__parse_ctx.begin(); + switch (__c) { + case _CharT('{'): + if (__parse_ctx.__consume() != _CharT('{')) { + __format::__handle_replacement_string(__parse_ctx, __ctx); + // At this point __parse_ctx.begin() points 1 character beyond the + // replacement string. Thus it should continue with the next iteration. + continue; + } + // {{ -> { + break; + + case _CharT('}'): + if (__parse_ctx.__consume() != _CharT('}')) + __throw_format_error("Stray '}' found"); + // }} -> } + break; + } + // When escaping {{ or }} it holds the first of the two characters. + __ctx.__write(__c); + __parse_ctx.__advance(); + } + return __ctx.out(); +} +} // namespace __format + +template +_LIBCPP_INLINE_VISIBILITY _OutIt +vformat_to(_OutIt __out_it, _VSTD::string_view __fmt, + format_args_t<_VSTD::type_identity_t<_OutIt>, char> __args) { + return __format::__vformat_to(__out_it, __fmt, __args); +} + +template +_OutIt +vformat_to(_OutIt __out_it, _VSTD::wstring_view __fmt, + format_args_t<_VSTD::type_identity_t<_OutIt>, wchar_t> __args) { + return __format::__vformat_to(__out_it, __fmt, __args); +} + +template +_LIBCPP_INLINE_VISIBILITY _OutIt format_to(_OutIt __out_it, + _VSTD::string_view __fmt, + const _Args&... __args) { + using _Context = basic_format_context<_OutIt, decltype(__fmt)::value_type>; + return vformat_to(__out_it, __fmt, make_format_args<_Context>(__args...)); +} + +template +_LIBCPP_INLINE_VISIBILITY _OutIt format_to(_OutIt __out_it, + _VSTD::wstring_view __fmt, + const _Args&... __args) { + using _Context = basic_format_context<_OutIt, decltype(__fmt)::value_type>; + return vformat_to(__out_it, __fmt, make_format_args<_Context>(__args...)); +} + +inline _LIBCPP_INLINE_VISIBILITY _VSTD::string vformat(_VSTD::string_view __fmt, + format_args __args) { + _VSTD::string __res; + vformat_to(_VSTD::back_inserter(__res), __fmt, __args); + return __res; +} + +inline _LIBCPP_INLINE_VISIBILITY _VSTD::wstring +vformat(_VSTD::wstring_view __fmt, wformat_args __args) { + _VSTD::wstring __res; + vformat_to(_VSTD::back_inserter(__res), __fmt, __args); + return __res; +} + +template +_LIBCPP_INLINE_VISIBILITY _VSTD::string format(_VSTD::string_view __fmt, + const _Args&... __args) { + return vformat(__fmt, make_format_args(__args...)); +} + +template +_LIBCPP_INLINE_VISIBILITY _VSTD::wstring format(_VSTD::wstring_view __fmt, + const _Args&... __args) { + return vformat(__fmt, make_wformat_args(__args...)); +} + +template +struct _LIBCPP_TEMPLATE_VIS format_to_n_result { + _OutIt out; + __iter_difference_t<_OutIt> size; +}; + +template +_LIBCPP_INLINE_VISIBILITY format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, __iter_difference_t<_OutIt> __n, + _VSTD::string_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::to_string is inefficient. + _VSTD::string __str = vformat(__fmt, make_format_args(__args...)); + __iter_difference_t<_OutIt> __s = __str.size(); + __iter_difference_t<_OutIt> __m = + _VSTD::clamp(__n, __iter_difference_t<_OutIt>(0), __s); + __out_it = _VSTD::copy_n(__str.begin(), __m, __out_it); + return {__out_it, __s}; +} + +template +_LIBCPP_INLINE_VISIBILITY format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, __iter_difference_t<_OutIt> __n, + _VSTD::wstring_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::to_string is inefficient. + _VSTD::wstring __str = vformat(__fmt, make_wformat_args(__args...)); + __iter_difference_t<_OutIt> __s = __str.size(); + __iter_difference_t<_OutIt> __m = + _VSTD::clamp(__n, __iter_difference_t<_OutIt>(0), __s); + __out_it = _VSTD::copy_n(__str.begin(), __m, __out_it); + return {__out_it, __s}; +} + +template +_LIBCPP_INLINE_VISIBILITY size_t formatted_size(_VSTD::string_view __fmt, + const _Args&... __args) { + // TODO FMT Improve PoC: using std::to_string is inefficient. + return vformat(__fmt, make_format_args(__args...)).size(); +} + +template +_LIBCPP_INLINE_VISIBILITY size_t formatted_size(_VSTD::wstring_view __fmt, + const _Args&... __args) { + // TODO FMT Improve PoC: using std::to_string is inefficient. + return vformat(__fmt, make_wformat_args(__args...)).size(); +} + #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) && !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.bool.pass.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, for each cv-unqualified arithmetic type ArithmeticT other +// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization +// - a specialization template<> struct formatter; +// Implemants ArithmeticT is +// - bool + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, std::basic_string fmt, + A arg) { + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Ctx ctx{std::back_inserter(result), std::make_format_args(arg)}; + formatter.format(arg, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using Out = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + Out (F::*format)(bool, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, "1"), MAKE_STRING(CharT, "}"), true); + test(MAKE_STRING(CharT, "0"), MAKE_STRING(CharT, "}"), false); +} + +int main(int, char**) { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c-array.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c-array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c-array.pass.cpp @@ -0,0 +1,105 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, the string type specializations +// - template struct formatter; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// This is based on the method found in +// clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp +template +struct Tester { + // This is not part of the real test, but is used the deduce the size of the input. + constexpr Tester(const char (&r)[N]) { __builtin_memcpy(text, r, N); } + char text[N]; + + // The size of the array shouldn't include the NUL character. + static const size_t size = N - 1; + + template + void test(const std::basic_string& expected, + const std::basic_string& fmt) const { + using Str = const CharT[size]; + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + std::basic_string buffer{text, text + N}; + // Note not too found of this hack + Str* data = reinterpret_cast(buffer.c_str()); + + Ctx ctx{std::back_inserter(result), std::make_format_args(*data)}; + formatter.format(*data, ctx); + assert(result == expected); + } +}; + +template +void test(std::basic_string expected, std::basic_string fmt) { + t.test(expected, fmt); +} + +template +void test() { + using Str = const CharT[42]; + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using Out = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + Out (F::*format)(Str, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test<" azAZ09,./<>?">(MAKE_STRING(CharT, " azAZ09,./<>?"), + MAKE_STRING(CharT, "}")); +} + +int main(int, char**) { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.c_string.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, the string type specializations +// - template<> struct formatter; +// - template<> struct formatter; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, std::basic_string fmt, + const CharT* arg) { + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + CharPtr data = const_cast(arg); + + Ctx ctx{std::back_inserter(result), std::make_format_args(data)}; + formatter.format(data, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using Out = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + Out (F::*format)(CharPtr, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, " azAZ09,./<>?"), MAKE_STRING(CharT, "}"), + MAKE_CSTRING(CharT, " azAZ09,./<>?")); +} + +template +void test() { + test(); + test(); +} +int main(int, char**) { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// template<> struct formatter; +// template<> struct formatter; +// template<> struct formatter; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, std::basic_string fmt, + A arg) { + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Ctx ctx{std::back_inserter(result), std::make_format_args(arg)}; + formatter.format(arg, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using Out = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + Out (F::*format)(T, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, "a"), MAKE_STRING(CharT, "}"), T('a')); + test(MAKE_STRING(CharT, "z"), MAKE_STRING(CharT, "}"), T('z')); + test(MAKE_STRING(CharT, "A"), MAKE_STRING(CharT, "}"), T('A')); + test(MAKE_STRING(CharT, "Z"), MAKE_STRING(CharT, "}"), T('Z')); + test(MAKE_STRING(CharT, "0"), MAKE_STRING(CharT, "}"), T('0')); + test(MAKE_STRING(CharT, "9"), MAKE_STRING(CharT, "}"), T('9')); +} + +int main(int, char**) { + test(); + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.floating_point.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, for each cv-unqualified arithmetic type ArithmeticT other +// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization +// template<> struct formatter; +// Tests ArithmeticT: +// - float +// - double +// - long double + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(Arithmetic input) { + std::basic_string fmt = MAKE_STRING(CharT, "{}"); + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Ctx ctx{std::back_inserter(result), std::make_format_args(input)}; + formatter.format(input, ctx); + std::string expected = std::to_string(input); + assert(result == std::basic_string(expected.begin(), expected.end())); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using OutIt = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + OutIt (F::*format)(Arithmetic, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(Arithmetic(-std::numeric_limits::max())); + test(Arithmetic(-std::numeric_limits::min())); + test(Arithmetic(-0.0)); + test(Arithmetic(0.0)); + test(Arithmetic(std::numeric_limits::min())); + test(Arithmetic(std::numeric_limits::max())); + if (sizeof(Arithmetic) > sizeof(float)) { + test(Arithmetic(-std::numeric_limits::max())); + test(Arithmetic(-std::numeric_limits::min())); + test(Arithmetic(std::numeric_limits::min())); + test(Arithmetic(std::numeric_limits::max())); + } + if (sizeof(Arithmetic) > sizeof(double)) { + test(Arithmetic(-std::numeric_limits::max())); + test(Arithmetic(-std::numeric_limits::min())); + test(Arithmetic(std::numeric_limits::min())); + test(Arithmetic(std::numeric_limits::max())); + } + + // TODO FMT Also test with special floating point values: +/-Inf NaN. +} + +template +void test() { + test(); + test(); + test(); +} + +int main(int, char**) { + test(); + test(); + // Note other character types aren't supported. + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.signed_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.signed_integral.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.signed_integral.pass.cpp @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, for each cv-unqualified arithmetic type ArithmeticT other +// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization +// template<> struct formatter; +// Tests ArithmeticT: +// - signed char +// - short +// - int +// - long +// - long long +// - __int128_t + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, Arithmetic input) { + std::basic_string fmt = MAKE_STRING(CharT, "{}"); + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Ctx ctx{std::back_inserter(result), std::make_format_args(input)}; + formatter.format(input, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using OutIt = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + OutIt (F::*format)(Arithmetic, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, "-128"), Arithmetic(-128)); + test(MAKE_STRING(CharT, "0"), Arithmetic(0)); + test(MAKE_STRING(CharT, "127"), Arithmetic(127)); + if (sizeof(Arithmetic) > 1) { + test(MAKE_STRING(CharT, "-32768"), Arithmetic(-32768)); + test(MAKE_STRING(CharT, "32767"), Arithmetic(32767)); + } + if (sizeof(Arithmetic) > 2) { + test(MAKE_STRING(CharT, "-2147483648"), Arithmetic(-2147483648)); + test(MAKE_STRING(CharT, "2147483647"), Arithmetic(2147483647)); + } + if (sizeof(Arithmetic) > 4) { + // -9223372036854775808 can't be used directly, it gives the following + // diagnostic: + // integer literal is too large to be represented in a signed integer type, + // interpreting as unsigned [-Werror,-Wimplicitly-unsigned-literal] + test(MAKE_STRING(CharT, "-9223372036854775808"), + Arithmetic(-9223372036854775807 - 1)); + test(MAKE_STRING(CharT, "9223372036854775807"), + Arithmetic(9223372036854775807)); + } + + // TODO FMT Implement the __int128_t minimum and maximum once the formatter + // can handle these values. +} + +template +void test() { + test(); + test(); + test(); + test(); + test(); +#ifndef _LIBCPP_HAS_NO_INT128 + test<__int128_t, CharT>(); +#endif +} + +int main(int, char**) { + test(); + test(); + // Note other character types aren't supported. + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, the string type specializations +// - template +// struct formatter, charT>; +// - template +// struct formatter, charT>; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, std::basic_string fmt, + std::basic_string arg) { + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Str data = arg; + Ctx ctx{std::back_inserter(result), std::make_format_args(data)}; + formatter.format(data, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using Out = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + Out (F::*format)(Arg, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, " azAZ09,./<>?"), MAKE_STRING(CharT, "}"), + MAKE_STRING(CharT, " azAZ09,./<>?")); +} + +template +void test() { + test, CharT, const std::basic_string&>(); + test, CharT, std::basic_string_view >(); +} + +int main(int, char**) { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_integral.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_integral.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// For each charT, for each cv-unqualified arithmetic type ArithmeticT other +// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization +// template<> struct formatter; +// Tests ArithmeticT: +// - unsigned char +// - unsigned short +// - unsigned +// - unsigned long +// - unsigned long long +// - __uint128_t + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(std::basic_string expected, Arithmetic input) { + std::basic_string fmt = MAKE_STRING(CharT, "{}"); + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + formatter.parse(parse_ctx); + + std::basic_string result; + using Ctx = std::basic_format_context< + std::back_insert_iterator >, CharT>; + + Ctx ctx{std::back_inserter(result), std::make_format_args(input)}; + formatter.format(input, ctx); + assert(result == expected); +} + +template +void test() { + using F = std::formatter; + static_assert(std::default_initializable); + static_assert(std::is_copy_constructible_v); + static_assert(std::is_move_constructible_v); + + static_assert(std::is_swappable_v); + + { + using OutIt = std::back_insert_iterator >; + using PC = std::basic_format_parse_context; + using C = std::basic_format_context; + + const CharT* (F::*parse)(PC&) = &F::parse; + assert(parse); + OutIt (F::*format)(Arithmetic, C&) = &F::format; + assert(format); + } + + // TODO FMT Add tests for std::formatter::parse() + + // TODO FMT Add tests for std::formatter::format() after + // implementing std::formatter::parse() and using different + // format-specifiers + + test(MAKE_STRING(CharT, "0"), Arithmetic(0)); + test(MAKE_STRING(CharT, "255"), Arithmetic(255)); + if (sizeof(Arithmetic) > 1) + test(MAKE_STRING(CharT, "65535"), Arithmetic(65535)); + if (sizeof(Arithmetic) > 2) + test(MAKE_STRING(CharT, "4294967295"), Arithmetic(4294967295)); + if (sizeof(Arithmetic) > 4) + test(MAKE_STRING(CharT, "8446744073709551615"), + Arithmetic(8446744073709551615)); + + // TODO FMT Implement the __uint128_t maximum once the formatter can handle + // these values. +} + +template +void test() { + test(); + test(); + test(); + test(); + test(); +#ifndef _LIBCPP_HAS_NO_INT128 + test<__uint128_t, CharT>(); +#endif +} + +int main(int, char**) { + test(); + test(); + // Note other character types aren't supported. + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__advance.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__advance.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__advance.pass.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// constexpr void __advance() noexcept + +#include +#include + +#include "test_macros.h" + +template +constexpr void test() { + auto f = std::array{CharT('a'), CharT('b'), CharT('c')}; + std::basic_format_parse_context ctx{ + std::basic_string_view(f.data(), 3)}; + auto it = ctx.begin(); + + ASSERT_NOEXCEPT(ctx.__advance()); + + ctx.__advance(); + assert(it + 1 == ctx.begin()); + assert(*ctx.begin() == CharT('b')); + + ctx.__advance(); + assert(it + 2 == ctx.begin()); + assert(*ctx.begin() == CharT('c')); + + ctx.__advance(); + assert(it + 3 == ctx.begin()); + assert(ctx.begin() == ctx.end()); +} + +bool constexpr test() { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__consume.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__consume.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__consume.pass.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// This test requires the dylib support introduced in D92214. +// XFAIL: with_system_cxx_lib=macosx10.15 +// XFAIL: with_system_cxx_lib=macosx10.14 +// XFAIL: with_system_cxx_lib=macosx10.13 +// XFAIL: with_system_cxx_lib=macosx10.12 +// XFAIL: with_system_cxx_lib=macosx10.11 +// XFAIL: with_system_cxx_lib=macosx10.10 +// XFAIL: with_system_cxx_lib=macosx10.9 + +// + +// constexpr _CharT __consume() + +#include +#include + +#include "test_macros.h" + +template +constexpr void test() { + auto f = std::array{CharT('a'), CharT('b'), CharT('c')}; + std::basic_format_parse_context ctx{ + std::basic_string_view(f.data(), 3)}; + auto it = ctx.begin(); + assert(ctx.__consume() == CharT('b')); + assert(it + 1 == ctx.begin()); + assert(ctx.__consume() == CharT('c')); + assert(it + 2 == ctx.begin()); + +#ifndef _LIBCPP_NO_EXCEPTIONS + if (!std::is_constant_evaluated()) { + try { + ctx.__consume(); + assert(false); + } catch (std::format_error& e) { + assert(strcmp(e.what(), "Unterminated format string") == 0); + return; + } + assert(false); + } +#endif +} + +bool constexpr test() { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__get_arg_id.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__get_arg_id.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/__get_arg_id.pass.cpp @@ -0,0 +1,107 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// This test requires the dylib support introduced in D92214. +// XFAIL: with_system_cxx_lib=macosx10.15 +// XFAIL: with_system_cxx_lib=macosx10.14 +// XFAIL: with_system_cxx_lib=macosx10.13 +// XFAIL: with_system_cxx_lib=macosx10.12 +// XFAIL: with_system_cxx_lib=macosx10.11 +// XFAIL: with_system_cxx_lib=macosx10.10 +// XFAIL: with_system_cxx_lib=macosx10.9 + +// + +// [[nodiscard]] constexpr size_t __get_arg_id() + +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +constexpr void test(size_t expected_id, size_t expected_length, + const CharT* fmt) { + std::basic_format_parse_context ctx{std::basic_string_view(fmt), -1u}; + auto it = ctx.begin(); + size_t id = ctx.__get_arg_id(); + assert(id == expected_id); + assert(it + expected_length == ctx.begin()); +} + +template +void test_exception(std::string_view what, std::basic_string fmt) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::basic_format_parse_context ctx{std::basic_string_view(fmt), + -1u}; + (void)ctx.__get_arg_id(); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; +#endif +} + +template +constexpr void test() { + test(0, 0, MAKE_CSTRING(CharT, ":")); + test(0, 0, MAKE_CSTRING(CharT, "}")); + + test(0, 1, MAKE_CSTRING(CharT, "0:")); + test(0, 1, MAKE_CSTRING(CharT, "0}")); + test(1, 1, MAKE_CSTRING(CharT, "1}")); + test(1, 1, MAKE_CSTRING(CharT, "1:")); + + test(10, 2, MAKE_CSTRING(CharT, "10:")); + test(100, 3, MAKE_CSTRING(CharT, "100:")); + test(1000, 4, MAKE_CSTRING(CharT, "1000:")); + + // This is not a valid replacement field, but it is a valid arg-id. + test(0, 1, MAKE_CSTRING(CharT, "0a")); + test(1, 1, MAKE_CSTRING(CharT, "1a")); + + if (!std::is_constant_evaluated()) { + test_exception("Found no arg-id, format-specifier, or '}'", + MAKE_STRING(CharT, "a")); + + test_exception("Unterminated format string", MAKE_STRING(CharT, "0")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "1")); + } +} + +bool constexpr test() { + // Only test char in constexpr evaluation. The issue is that make_string + // can't be constexpr. + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format.locale.pass.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// template +// string format(const locale& loc, string_view fmt, const Args&... args); +// template +// wstring format(const locale& loc, wstring_view fmt, const Args&... args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/format.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format.pass.cpp @@ -0,0 +1,196 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// template +// string format(string_view fmt, const Args&... args); +// template +// wstring format(wstring_view fmt, const Args&... args); + +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + std::basic_string out = std::format(fmt, args...); + assert(out == expected); +} + +template +void test_exception(std::string_view what, std::basic_string fmt, + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::format(fmt, args...); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// template +// Out format_to(Out out, const locale& loc, +// string_view fmt, const Args&... args); +// template +// Out format_to(Out out, const locale& loc, +// wstring_view fmt, const Args&... args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp @@ -0,0 +1,232 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// The tests write fixed size buffer, make sure it remains in bounds. +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEBUG=1 +// UNSUPPORTED: libcxx-no-debug-mode + +// + +// template +// Out format_to(Out out, string_view fmt, const Args&... args); +// template +// Out format_to(Out out, wstring_view fmt, const Args&... args); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +bool operator==(const std::list& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator==(const std::vector& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + { + std::basic_string out(expected.size(), CharT(' ')); + auto it = std::format_to(out.begin(), fmt, args...); + assert(it == out.end()); + assert(out == expected); + } + { + std::list out; + std::format_to(std::back_inserter(out), fmt, args...); + assert(out == expected); + } + { + std::vector out; + std::format_to(std::back_inserter(out), fmt, args...); + assert(out == expected); + } +} + +template +void test_exception(std::string_view what, std::basic_string(fmt), + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::basic_string out; + std::format_to(std::back_inserter(out), fmt, args...); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.locale.pass.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// const locale& loc, string_view fmt, +// const Args&... args); +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// const locale& loc, wstring_view fmt, +// const Args&... args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format_to_n.pass.cpp @@ -0,0 +1,274 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// string_view fmt, const Args&... args); +// template +// format_to_n_result format_to_n(Out out, iter_difference_t n, +// wstring_view fmt, const Args&... args); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +bool operator==(const std::list& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator==(const std::vector& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + { + std::list out; + std::format_to_n_result result = + std::format_to_n(std::back_inserter(out), 0, fmt, args...); + // To avoid signedness warnings make sure formatted_size uses the same type + // as result.size. + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(fmt, args...); + + assert(result.size == formatted_size); + assert(out.empty()); + } + { + std::vector out; + std::format_to_n_result result = + std::format_to_n(std::back_inserter(out), 5, fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(fmt, args...); + diff_type size = std::min(5, formatted_size); + + assert(result.size == formatted_size); + assert(out == expected.substr(0, size)); + } + { + std::basic_string out; + std::format_to_n_result result = + std::format_to_n(std::back_inserter(out), 1000, fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(fmt, args...); + diff_type size = std::min(1000, formatted_size); + + assert(result.size == formatted_size); + assert(out == expected.substr(0, size)); + } + { + // Test the returned iterator. + std::basic_string out(10, CharT(' ')); + std::format_to_n_result result = + std::format_to_n(out.begin(), 10, fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(fmt, args...); + diff_type size = std::min(10, formatted_size); + + assert(result.size == formatted_size); + assert(result.out == out.begin() + size); + assert(out.substr(0, size) == expected.substr(0, size)); + } + { + static_assert(std::is_signed_v >, + "If the difference type isn't negative the test will fail " + "due to using a large positive value."); + CharT buffer[1] = {CharT(0)}; + std::format_to_n_result result = std::format_to_n(buffer, -1, fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(fmt, args...); + + assert(result.size == formatted_size); + assert(result.out == buffer); + assert(buffer[0] == CharT(0)); + } +} + +template +void test_exception(std::string_view what, std::basic_string(fmt), + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::basic_string out; + std::format_to_n(std::back_inserter(out), 0, fmt, args...); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.locale.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// template +// size_t formatted_size(const locale& loc, +// string_view fmt, const Args&... args); +// template +// size_t formatted_size(const locale& loc, +// wstring_view fmt, const Args&... args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/formatted_size.pass.cpp @@ -0,0 +1,196 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// template +// size_t formatted_size(string_view fmt, const Args&... args); +// template +// size_t formatted_size(wstring_view fmt, const Args&... args); + +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + size_t size = std::formatted_size(fmt, args...); + assert(size == expected.size()); +} + +template +void test_exception(std::string_view what, std::basic_string fmt, + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::formatted_size(fmt, args...); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/vformat.locale.pass.cpp @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// string vformat(const locale& loc, string_view fmt, format_args args); +// wstring vformat(const locale& loc, wstring_view fmt, wformat_args args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/vformat.pass.cpp @@ -0,0 +1,200 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// string vformat(string_view fmt, format_args args); +// wstring vformat(wstring_view fmt, wformat_args args); + +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + std::basic_string out = std::vformat( + fmt, std::make_format_args >, CharT> >( + args...)); + assert(out == expected); +} + +template +void test_exception(std::string_view what, std::basic_string fmt, + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::vformat( + fmt, std::make_format_args >, CharT> >( + args...)); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.locale.pass.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts +// UNSUPPORTED: libcpp-has-no-localization + +// + +// template +// Out vformat_to(Out out, const locale& loc, string_view fmt, +// format_args_t, char> args); +// template +// Out vformat_to(Out out, const locale& loc, wstring_view fmt, +// format_args_t, wchar_t> args); + +// TODO FMT Implement tests after implementing this function in + +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/vformat_to.pass.cpp @@ -0,0 +1,247 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// The tests write fixed size buffer, make sure it remains in bounds. +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DEBUG=1 +// UNSUPPORTED: libcxx-no-debug-mode + +// + +// template +// Out vformat_to(Out out, string_view fmt, +// format_args_t, char> args); +// template +// Out vformat_to(Out out, wstring_view fmt, +// format_args_t, wchar_t> args); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +// Helper functions for this specific test. + +template +bool operator==(const std::list& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator==(const std::vector& lhs, + const std::basic_string& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +void test(std::basic_string expected, std::basic_string fmt, + const Args&... args) { + { + std::basic_string out(expected.size(), CharT(' ')); + auto it = std::vformat_to( + out.begin(), fmt, + std::make_format_args::iterator, CharT> >(args...)); + assert(it == out.end()); + assert(out == expected); + } + { + std::list out; + std::vformat_to( + std::back_inserter(out), fmt, + std::make_format_args >, CharT> >(args...)); + assert(out == expected); + } + { + std::vector out; + std::vformat_to( + std::back_inserter(out), fmt, + std::make_format_args >, CharT> >(args...)); + assert(out == expected); + } +} + +template +void test_exception(std::string_view what, std::basic_string(fmt), + const Args&... args) { +#ifndef _LIBCPP_NO_EXCEPTIONS + try { + std::basic_string out; + std::vformat_to( + std::back_inserter(out), fmt, + std::make_format_args >, CharT> >( + args...)); + assert(false); + } catch (std::format_error& e) { + assert(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)args; +#endif +} + +// General format functions test code. + +void test_char_to_wchar() { + test(MAKE_STRING(wchar_t, "hello 09azA"), + MAKE_STRING(wchar_t, "hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // Test escaping. + test(MAKE_STRING(CharT, "{"), MAKE_STRING(CharT, "{{")); + test(MAKE_STRING(CharT, "}"), MAKE_STRING(CharT, "}}")); + + // Test argument ID. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {0:}{1:}"), + false, true); + test(MAKE_STRING(CharT, "hello 10"), MAKE_STRING(CharT, "hello {1:}{0:}"), + false, true); + + // Test invalid format strings. + test_exception("Unterminated format string", MAKE_STRING(CharT, "{")); + test_exception("Unterminated format string", MAKE_STRING(CharT, "}")); + test_exception("Stray '}' found", MAKE_STRING(CharT, "} ")); + + // Test char format argument. + // Note the char to wchar_t formatting is tested seperately. + test(MAKE_STRING(CharT, "hello 09azAZ!"), + MAKE_STRING(CharT, "hello {}{}{}{}{}{}{}"), CharT('0'), CharT('9'), + CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!')); + + // Test string format argument. + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(MAKE_STRING(CharT, "hello 09azAZ!"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string data = MAKE_STRING(CharT, "world"); + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + { + std::basic_string buffer = MAKE_STRING(CharT, "world"); + std::basic_string_view data = buffer; + test(MAKE_STRING(CharT, "hello world"), MAKE_STRING(CharT, "hello {}"), + data); + } + + // Test Boolean format argument. + test(MAKE_STRING(CharT, "hello 01"), MAKE_STRING(CharT, "hello {}{}"), false, + true); + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__int128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string min = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::min()); + test(min, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = std::format( + MAKE_STRING(CharT, "{}"), std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // Test signed integral format argument. + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(MAKE_STRING(CharT, "hello 42"), MAKE_STRING(CharT, "hello {}"), + static_cast<__uint128_t>(42)); + { + // Note 128 bit support is only party implemented test the range + // conditions here. + std::basic_string max = + std::format(MAKE_STRING(CharT, "{}"), + std::numeric_limits::max()); + test(max, MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128 bit value is outside of implemented range", + MAKE_STRING(CharT, "{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // Test floating point format argument. + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); + test(MAKE_STRING(CharT, "hello 42.000000"), MAKE_STRING(CharT, "hello {}"), + static_cast(42)); +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} diff --git a/libcxx/test/support/make_string.h b/libcxx/test/support/make_string.h --- a/libcxx/test/support/make_string.h +++ b/libcxx/test/support/make_string.h @@ -39,13 +39,13 @@ const char16_t* u16; const char32_t* u32; - operator const char*() const { return s; } - operator const wchar_t*() const { return w; } + constexpr operator const char*() const { return s; } + constexpr operator const wchar_t*() const { return w; } #if TEST_STD_VER > 17 && defined(__cpp_char8_t) - operator const char8_t*() const { return u8; } + constexpr operator const char8_t*() const { return u8; } #endif - operator const char16_t*() const { return u16; } - operator const char32_t*() const { return u32; } + constexpr operator const char16_t*() const { return u16; } + constexpr operator const char32_t*() const { return u32; } }; // Helper to convert a const char* string to a basic_string. @@ -56,4 +56,10 @@ static_cast(MultiStringType MKSTR(Str)) \ } +// Helper to convert a const char* string to a const CharT*. +// This helper is used in unit tests to make them generic. The input should be +// valid ASCII which means the input is also valid UTF-8. +#define MAKE_CSTRING(CharT, Str) \ + static_cast(MultiStringType MKSTR(Str)) + #endif