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/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -134,6 +134,8 @@ __format/format_error.h __format/format_fwd.h __format/format_parse_context.h + __format/format_string.h + __format/formatter.h __function_like.h __functional_base __functional/binary_function.h diff --git a/libcxx/include/__format/format_string.h b/libcxx/include/__format/format_string.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/format_string.h @@ -0,0 +1,174 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMAT_STRING_H +#define _LIBCPP___FORMAT_FORMAT_STRING_H + +#include <__config> +#include <__debug> +#include <__format/format_error.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format { + +template +struct _LIBCPP_TEMPLATE_VIS __parse_number_result { + const _CharT* __ptr; + uint32_t __value; +}; + +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_number(const _CharT* __begin, const _CharT* __end); + +/** + * The maximum value of a numeric argument. + * + * This is used for: + * * arg-id + * * width as value or arg-id. + * * precision as value or arg-id. + * + * The value is compatible with the maximum formatting width and precision + * using the `%*` syntax on a 32-bit system. + */ +inline constexpr uint32_t __number_max = INT32_MAX; + +namespace __detail { +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_zero(const _CharT* __begin, const _CharT*, auto& __parse_ctx) { + __parse_ctx.check_arg_id(0); + return {++__begin, 0}; // can never be larger than the maximum. +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_automatic(const _CharT* __begin, const _CharT*, auto& __parse_ctx) { + size_t __value = __parse_ctx.next_arg_id(); + _LIBCPP_ASSERT(__value <= __number_max, + "Compilers don't support this number of arguments"); + + return {__begin, uint32_t(__value)}; +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_manual(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + __parse_number_result<_CharT> __r = __parse_number(__begin, __end); + __parse_ctx.check_arg_id(__r.__value); + return __r; +} + +} // namespace __detail + +/** + * Parses a number. + * + * The number is used for the 31-bit values @em width and @em precision. This + * allows a maximum value of 2147483647. + */ +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_number(const _CharT* __begin, const _CharT* __end_input) { + static_assert(__format::__number_max == INT32_MAX, + "The algorithm is implemented based on this value."); + /* + * Limit the input to 9 digits, otherwise we need two checks during every + * iteration: + * - Are we at the end of the input? + * - Does the value exceed width of an uint32_t? (Switching to uint64_t would + * have the same issue, but with a higher maximum.) + */ + const _CharT* __end = __end_input - __begin > 9 ? __begin + 9 : __end_input; + uint32_t __value = *__begin - _CharT('0'); + while (++__begin != __end) { + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + return {__begin, __value}; + + __value = __value * 10 + *__begin - _CharT('0'); + } + + if (__begin != __end_input && *__begin >= _CharT('0') && + *__begin <= _CharT('9')) { + + /* + * There are more than 9 digits, do additional validations: + * - Does the 10th digit exceed the maximum allowed value? + * - Are there more than 10 digits? + * (More than 10 digits always overflows the maximum.) + */ + uint64_t __v = uint64_t(__value) * 10 + *__begin++ - _CharT('0'); + if (__v > __number_max || + (__begin != __end_input && *__begin >= _CharT('0') && + *__begin <= _CharT('9'))) + __throw_format_error("The numeric value of the format-spec is too large"); + + __value = __v; + } + + return {__begin, __value}; +} + +/** + * Multiplexer for all parse functions. + * + * The parser will return a pointer beyond the last consumed character. This + * should be the closing '}' of the arg-id. + */ +template +_LIBCPP_HIDE_FROM_ABI constexpr __parse_number_result<_CharT> +__parse_arg_id(const _CharT* __begin, const _CharT* __end, auto& __parse_ctx) { + switch (*__begin) { + case _CharT('0'): + return __detail::__parse_zero(__begin, __end, __parse_ctx); + + case _CharT(':'): + // This case is conditionally valid. It's allowed in an arg-id in the + // replacement-field, but not in the std-format-spec. The caller can + // provide a better diagnostic, so accept it here unconditionally. + case _CharT('}'): + return __detail::__parse_automatic(__begin, __end, __parse_ctx); + } + if (*__begin < _CharT('0') || *__begin > _CharT('9')) + __throw_format_error( + "The arg-id of the format-spec starts with an invalid character"); + + return __detail::__parse_manual(__begin, __end, __parse_ctx); +} + +} // namespace __format + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_STRING_H diff --git a/libcxx/include/__format/formatter.h b/libcxx/include/__format/formatter.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/formatter.h @@ -0,0 +1,64 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMATTER_H +#define _LIBCPP___FORMAT_FORMATTER_H + +#include <__availability> +#include <__config> +#include <__format/format_error.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// Currently not implemented specializations throw an exception when used. This +// does not conform to 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 template. +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter { + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI auto parse(auto& __parse_ctx) + -> decltype(__parse_ctx.begin()) { + __throw(); + } + + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI auto format(_Tp, auto& __ctx) + -> decltype(__ctx.out()) { + __throw(); + } + +private: + _LIBCPP_NORETURN _LIBCPP_HIDE_FROM_ABI void __throw() { + __throw_format_error("Argument type not implemented yet"); + } +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMATTER_H diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -54,6 +54,85 @@ 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); + template + string format(const locale& loc, string_view fmt, const Args&... args); + template + wstring format(const locale& loc, wstring_view fmt, const Args&... args); + + string vformat(string_view fmt, format_args args); + wstring vformat(wstring_view fmt, wformat_args args); + string vformat(const locale& loc, string_view fmt, format_args args); + wstring vformat(const locale& loc, 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 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); + + 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 + 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); + + 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 + 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); + + template + size_t formatted_size(string_view fmt, const Args&... args); + template + size_t formatted_size(wstring_view fmt, const Args&... args); + 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); + + // [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 { @@ -191,12 +270,24 @@ #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT) #include <__config> +#include <__debug> #include <__format/format_arg.h> #include <__format/format_args.h> #include <__format/format_context.h> #include <__format/format_error.h> #include <__format/format_parse_context.h> +#include <__format/format_string.h> +#include <__format/formatter.h> +#include <__variant/monostate.h> #include +#include +#include +#include +#include + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -241,6 +332,597 @@ return _VSTD::make_format_args(__args...); } +namespace __format { +template +struct _LIBCPP_TEMPLATE_VIS __formatter_char { + _LIBCPP_HIDE_FROM_ABI + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_HIDE_FROM_ABI + 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_HIDE_FROM_ABI + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_HIDE_FROM_ABI + 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_HIDE_FROM_ABI + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement this function. + return __parse_ctx.begin(); + } + + _LIBCPP_HIDE_FROM_ABI + auto format(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(is_arithmetic_v<_Tp> && + !same_as<_Tp, bool>) struct _LIBCPP_HIDE_FROM_ABI + __formatter_arithmetic { + _LIBCPP_HIDE_FROM_ABI + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement + return __parse_ctx.begin(); + } + + _LIBCPP_HIDE_FROM_ABI + auto format(_Tp __value, auto& __ctx) -> decltype(__ctx.out()) { + return __handle_format(__value, __ctx); + } + +private: + template + _LIBCPP_HIDDEN static string + __convert(_Uv __value) requires(same_as<_CharT, char>) { + return _VSTD::to_string(__value); + } + template + _LIBCPP_HIDDEN static wstring + __convert(_Uv __value) requires(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(!same_as<_Uv, __int128_t> && !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(same_as<_Uv, __int128_t> || + same_as<_Uv, __uint128_t>) { + using _To = conditional_t, long long, unsigned long long>; + // TODO FMT Implement full 128-bit support. + if (__value < numeric_limits<_To>::min() || + __value > 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 + +// These specializations are helper stubs and not proper formatters. +// TODO FMT Implement the proper formatter specializations. + +// [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_HIDE_FROM_ABI 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_HIDE_FROM_ABI 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, _CharT> + : public __format::__formatter_string<_CharT> { + using _Base = __format::__formatter_string<_CharT>; + + _LIBCPP_HIDE_FROM_ABI auto + format(const 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, _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_HIDE_FROM_ABI + auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) { + // TODO FMT Implement + return __parse_ctx.begin(); + } + + _LIBCPP_HIDE_FROM_ABI + 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. +// TODO FMT There are no replacements for the floating point stubs due to not +// having floating point support in std::to_chars yet. These stubs aren't +// removed since they are useful for developing the real versions. +// Ultimately the stubs should be implemented properly and this code can be +// removed. +#if 0 +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter + : public __format::__formatter_arithmetic {}; +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT + formatter + : public __format::__formatter_arithmetic {}; +#endif + +namespace __format { + +template +_LIBCPP_HIDE_FROM_ABI const _CharT* +__handle_replacement_field(const _CharT* __begin, const _CharT* __end, + _ParseCtx& __parse_ctx, _Ctx& __ctx) { + __format::__parse_number_result __r = + __format::__parse_arg_id(__begin, __end, __parse_ctx); + + switch (*__r.__ptr) { + case _CharT(':'): + // The arg-id has a format-specifier, advance the input to the format-spec. + __parse_ctx.advance_to(__r.__ptr + 1); + break; + case _CharT('}'): + // The arg-id has no format-specifier. + __parse_ctx.advance_to(__r.__ptr); + break; + default: + __throw_format_error( + "The replacement field arg-id should terminate at a ':' or '}'"); + } + + _VSTD::visit_format_arg( + [&](auto __arg) { + if constexpr (same_as) + __throw_format_error("Argument index out of bounds"); + else { + formatter __formatter; + __parse_ctx.advance_to(__formatter.parse(__parse_ctx)); + __ctx.advance_to(__formatter.format(__arg, __ctx)); + } + }, + __ctx.arg(__r.__value)); + + __begin = __parse_ctx.begin(); + if (__begin == __end || *__begin != _CharT('}')) + __throw_format_error("The replacement field misses a terminating '}'"); + + return ++__begin; +} + +template +_LIBCPP_HIDE_FROM_ABI typename _Ctx::iterator +__vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) { + using _CharT = typename _ParseCtx::char_type; + static_assert(same_as); + + const _CharT* __begin = __parse_ctx.begin(); + const _CharT* __end = __parse_ctx.end(); + typename _Ctx::iterator __out_it = __ctx.out(); + while (__begin != __end) { + switch (*__begin) { + case _CharT('{'): + ++__begin; + if (__begin == __end) + __throw_format_error("The format string terminates at a '{'"); + + if (*__begin != _CharT('{')) [[likely]] { + __ctx.advance_to(_VSTD::move(__out_it)); + __begin = + __handle_replacement_field(__begin, __end, __parse_ctx, __ctx); + __out_it = __ctx.out(); + + // The output is written and __begin points to the next character. So + // start the next iteration. + continue; + } + // The string is an escape character. + break; + + case _CharT('}'): + ++__begin; + if (__begin == __end || *__begin != _CharT('}')) + __throw_format_error( + "The format string contains an invalid escape sequence"); + + break; + } + + // Copy the character to the output verbatim. + *__out_it++ = *__begin++; + } + return __out_it; +} + +} // namespace __format + +template +requires(output_iterator<_OutIt, const _CharT&>) _LIBCPP_HIDE_FROM_ABI _OutIt + __vformat_to(_OutIt __out_it, basic_string_view<_CharT> __fmt, + format_args_t, _CharT> __args) { + return __format::__vformat_to( + basic_format_parse_context{__fmt, __args.__size()}, + _VSTD::__format_context_create(_VSTD::move(__out_it), __args)); +} + +template _OutIt> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +vformat_to(_OutIt __out_it, string_view __fmt, + format_args_t, char> __args) { + return _VSTD::__vformat_to(_VSTD::move(__out_it), __fmt, __args); +} + +template _OutIt> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +vformat_to(_OutIt __out_it, wstring_view __fmt, + format_args_t, wchar_t> __args) { + return _VSTD::__vformat_to(_VSTD::move(__out_it), __fmt, __args); +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +format_to(_OutIt __out_it, string_view __fmt, const _Args&... __args) { + return _VSTD::vformat_to( + _VSTD::move(__out_it), __fmt, + _VSTD::make_format_args>(__args...)); +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +format_to(_OutIt __out_it, wstring_view __fmt, const _Args&... __args) { + return _VSTD::vformat_to( + _VSTD::move(__out_it), __fmt, + _VSTD::make_format_args>( + __args...)); +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string +vformat(string_view __fmt, format_args __args) { + string __res; + _VSTD::vformat_to(_VSTD::back_inserter(__res), __fmt, __args); + return __res; +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring +vformat(wstring_view __fmt, wformat_args __args) { + wstring __res; + _VSTD::vformat_to(_VSTD::back_inserter(__res), __fmt, __args); + return __res; +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string +format(string_view __fmt, const _Args&... __args) { + return _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...)); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring +format(wstring_view __fmt, const _Args&... __args) { + return _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...)); +} + +template +struct _LIBCPP_TEMPLATE_VIS format_to_n_result { + _OutIt out; + iter_difference_t<_OutIt> size; +}; + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, string_view __fmt, + const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + string __str = _VSTD::vformat(__fmt, _VSTD::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, _VSTD::move(__out_it)); + return {_VSTD::move(__out_it), __s}; +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wstring_view __fmt, + const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + wstring __str = _VSTD::vformat(__fmt, _VSTD::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, _VSTD::move(__out_it)); + return {_VSTD::move(__out_it), __s}; +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t +formatted_size(string_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + return _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...)).size(); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t +formatted_size(wstring_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + return _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...)).size(); +} + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + +template +requires(output_iterator<_OutIt, const _CharT&>) _LIBCPP_HIDE_FROM_ABI _OutIt + __vformat_to(_OutIt __out_it, locale __loc, basic_string_view<_CharT> __fmt, + format_args_t, _CharT> __args) { + return __format::__vformat_to( + basic_format_parse_context{__fmt, __args.__size()}, + _VSTD::__format_context_create(_VSTD::move(__out_it), __args, + _VSTD::move(__loc))); +} + +template _OutIt> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +vformat_to(_OutIt __out_it, locale __loc, string_view __fmt, + format_args_t, char> __args) { + return _VSTD::__vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt, + __args); +} + +template _OutIt> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt +vformat_to(_OutIt __out_it, locale __loc, wstring_view __fmt, + format_args_t, wchar_t> __args) { + return _VSTD::__vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt, + __args); +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt format_to( + _OutIt __out_it, locale __loc, string_view __fmt, const _Args&... __args) { + return _VSTD::vformat_to( + _VSTD::move(__out_it), _VSTD::move(__loc), __fmt, + _VSTD::make_format_args>(__args...)); +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt format_to( + _OutIt __out_it, locale __loc, wstring_view __fmt, const _Args&... __args) { + return _VSTD::vformat_to( + _VSTD::move(__out_it), _VSTD::move(__loc), __fmt, + _VSTD::make_format_args>( + __args...)); +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string +vformat(locale __loc, string_view __fmt, format_args __args) { + string __res; + _VSTD::vformat_to(_VSTD::back_inserter(__res), _VSTD::move(__loc), __fmt, + __args); + return __res; +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring +vformat(locale __loc, wstring_view __fmt, wformat_args __args) { + wstring __res; + _VSTD::vformat_to(_VSTD::back_inserter(__res), _VSTD::move(__loc), __fmt, + __args); + return __res; +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string +format(locale __loc, string_view __fmt, const _Args&... __args) { + return _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::make_format_args(__args...)); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring +format(locale __loc, wstring_view __fmt, const _Args&... __args) { + return _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::make_wformat_args(__args...)); +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, + string_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + string __str = _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::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, _VSTD::move(__out_it)); + return {_VSTD::move(__out_it), __s}; +} + +template _OutIt, class... _Args> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt> +format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc, + wstring_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + wstring __str = _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::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, _VSTD::move(__out_it)); + return {_VSTD::move(__out_it), __s}; +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t +formatted_size(locale __loc, string_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + return _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::make_format_args(__args...)) + .size(); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t +formatted_size(locale __loc, wstring_view __fmt, const _Args&... __args) { + // TODO FMT Improve PoC: using std::string is inefficient. + return _VSTD::vformat(_VSTD::move(__loc), __fmt, + _VSTD::make_wformat_args(__args...)) + .size(); +} + +#endif + #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -441,6 +441,8 @@ module format_error { private header "__format/format_error.h" } module format_fwd { private header "__format/format_fwd.h" } module format_parse_context { private header "__format/format_parse_context.h" } + module format_string { private header "__format/format_string.h" } + module formatter { private header "__format/formatter.h" } } } module forward_list { diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/format_string.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_string.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_string.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/format_string.h'}} +#include <__format/format_string.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/formatter.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/formatter.h'}} +#include <__format/formatter.h> diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp rename from libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp rename to libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp rename from libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp rename to libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp --- a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp @@ -24,10 +24,14 @@ #include "test_macros.h" void test(const auto& store) { +#if _LIBCPP_VERSION for (const auto& arg : store.__args) { assert(arg); assert(static_cast(arg)); } +#else + (void)store; +#endif } template diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.bool.pass.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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 +// +// This file tests with `ArithmeticT = bool`, for each valid `charT`. + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, bool arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, bool arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_boolean() { + test_termination_condition(STR("1"), STR("}"), true); + test_termination_condition(STR("0"), STR("}"), false); +} + +int main(int, char**) { + test_boolean(); + test_boolean(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.c_string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.c_string.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.c_string.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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" + +#define STR(S) MAKE_STRING(CharT, S) +#define CSTR(S) MAKE_CSTRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, const CharT* a) { + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto* arg = const_cast(a); + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, const CharT* arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_char_pointer() { + using CharT = std::remove_cv_t>; + + test_termination_condition(STR(" azAZ09,./<>?"), STR("}"), + CSTR(" azAZ09,./<>?")); + + std::basic_string s(CSTR("abc\0abc"), 7); + test_termination_condition(STR("abc"), STR("}"), s.c_str()); +} + +int main(int, char**) { + test_char_pointer(); + test_char_pointer(); + test_char_pointer(); + test_char_pointer(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp @@ -0,0 +1,83 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// The specializations +// template<> struct formatter; +// template<> struct formatter; +// template<> struct formatter; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, ArgumentT arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, ArgumentT arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_char_type() { + test_termination_condition(STR("a"), STR("}"), ArgumentT('a')); + test_termination_condition(STR("z"), STR("}"), ArgumentT('z')); + test_termination_condition(STR("A"), STR("}"), ArgumentT('A')); + test_termination_condition(STR("Z"), STR("}"), ArgumentT('Z')); + test_termination_condition(STR("0"), STR("}"), ArgumentT('0')); + test_termination_condition(STR("9"), STR("}"), ArgumentT('9')); +} + +int main(int, char**) { + test_char_type(); + test_char_type(); + test_char_type(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.const_char_array.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.const_char_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.const_char_array.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 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// UNSUPPORTED: clang-11 +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 +// UNSUPPORTED: apple-clang-12 + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// For each `charT`, the string type specializations +// template struct formatter; + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) +#define CSTR(S) MAKE_CSTRING(CharT, S) + +// 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_view& fmt) const { + using Str = const CharT[size]; + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + std::basic_string result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + std::basic_string buffer{text, text + N}; + // Note not too found of this hack + Str* data = reinterpret_cast(buffer.c_str()); + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(*data)); + formatter.format(*data, format_ctx); + assert(result == expected); + } + + template + void test_termination_condition(const std::basic_string& expected, + const std::basic_string& f) const { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt); + fmt.remove_suffix(1); + test(expected, fmt); + } +}; + +template +void test_helper_wrapper(std::basic_string expected, + std::basic_string fmt) { + t.test_termination_condition(expected, fmt); +} + +template +void test_array() { + test_helper_wrapper<" azAZ09,./<>?">(STR(" azAZ09,./<>?"), STR("}")); + + std::basic_string s(CSTR("abc\0abc"), 7); + test_helper_wrapper<"abc\0abc">(s, STR("}")); +} + +int main(int, char**) { + test_array(); + test_array(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.floating_point.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.floating_point.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.floating_point.pass.cpp @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// UNSUPPORTED: LIBCXX-DEBUG-FIXME + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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 +// +// This file tests with `ArithmeticT = floating-point`, for each valid `charT`. +// Where `floating-point` is one of: +// - float +// - double +// - long double + +// TODO FMT Enable after floating-point support has been enabled +#if 0 +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +template +void test(StringViewT fmt, ArithmeticT arg) { + using CharT = typename StringViewT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + std::basic_string result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + std::string expected = std::to_string(arg); + assert(result == std::basic_string(expected.begin(), expected.end())); +} + +template +void test_termination_condition(StringT f, ArithmeticT arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(fmt, arg); + fmt.remove_suffix(1); + test(fmt, arg); +} + +template +void test_float_type() { + using A = ArithmeticT; + test_termination_condition(STR("}"), A(-std::numeric_limits::max())); + test_termination_condition(STR("}"), A(-std::numeric_limits::min())); + test_termination_condition(STR("}"), A(-0.0)); + test_termination_condition(STR("}"), A(0.0)); + test_termination_condition(STR("}"), A(std::numeric_limits::min())); + test_termination_condition(STR("}"), A(std::numeric_limits::max())); + if (sizeof(A) > sizeof(float)) { + test_termination_condition(STR("}"), + A(-std::numeric_limits::max())); + test_termination_condition(STR("}"), + A(-std::numeric_limits::min())); + test_termination_condition(STR("}"), A(std::numeric_limits::min())); + test_termination_condition(STR("}"), A(std::numeric_limits::max())); + } + if (sizeof(A) > sizeof(double)) { + test_termination_condition(STR("}"), + A(-std::numeric_limits::max())); + test_termination_condition(STR("}"), + A(-std::numeric_limits::min())); + test_termination_condition(STR("}"), + A(std::numeric_limits::min())); + test_termination_condition(STR("}"), + A(std::numeric_limits::max())); + } + + // TODO FMT Also test with special floating point values: +/-Inf NaN. +} + +template +void test_all_float_types() { + test_float_type(); + test_float_type(); + test_float_type(); +} + +int main(int, char**) { + test_all_float_types(); + test_all_float_types(); + + return 0; +} +#else +int main(int, char**) { return 0; } +#endif diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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 +// +// This file tests with `ArithmeticT = signed integer`, for each valid `charT`. +// Where `signed integer` is one of: +// - signed char +// - short +// - int +// - long +// - long long +// - __int128_t + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, ArithmeticT arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, ArithmeticT arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_signed_integral_type() { + using A = Arithmetic; + test_termination_condition(STR("-128"), STR("}"), A(-128)); + test_termination_condition(STR("0"), STR("}"), A(0)); + test_termination_condition(STR("127"), STR("}"), A(127)); + if (sizeof(A) > 1) { + test_termination_condition(STR("-32768"), STR("}"), A(-32768)); + test_termination_condition(STR("32767"), STR("}"), A(32767)); + } + if (sizeof(A) > 2) { + test_termination_condition(STR("-2147483648"), STR("}"), A(-2147483648)); + test_termination_condition(STR("2147483647"), STR("}"), A(2147483647)); + } + if (sizeof(A) > 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_termination_condition(STR("-9223372036854775808"), STR("}"), + A(-9223372036854775807 - 1)); + test_termination_condition(STR("9223372036854775807"), STR("}"), + A(9223372036854775807)); + } + + // TODO FMT Implement the __int128_t minimum and maximum once the formatter + // can handle these values. +} + +template +void test_all_signed_integral_types() { + test_signed_integral_type(); + test_signed_integral_type(); + test_signed_integral_type(); + test_signed_integral_type(); + test_signed_integral_type(); +#ifndef _LIBCPP_HAS_NO_INT128 + test_signed_integral_type<__int128_t, CharT>(); +#endif +} + +int main(int, char**) { + test_all_signed_integral_types(); + test_all_signed_integral_types(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.string.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.string.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.string.pass.cpp @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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" + +#define STR(S) MAKE_STRING(CharT, S) +#define CSTR(S) MAKE_CSTRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, StringT a) { + static_assert( + std::same_as::value_type> && + std::same_as); + using CharT = typename T::value_type; + + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + ArgumentT arg = a; + auto format_ctx = std::__format_context_create( + out, std::make_format_args(std::forward(arg))); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, StringT arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_string_type() { + static_assert(std::same_as::value_type>); + using CharT = typename T::value_type; + + test_termination_condition(STR(" azAZ09,./<>?"), STR("}"), + STR(" azAZ09,./<>?")); + + std::basic_string s(CSTR("abc\0abc"), 7); + test_termination_condition(s, STR("}"), s); +} + +template +void test_all_string_types() { + test_string_type, const std::basic_string&>(); + test_string_type, + std::basic_string_view>(); +} + +int main(int, char**) { + test_all_string_types(); + test_all_string_types(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format + +// + +// [format.formatter.spec]: +// Each header that declares the template `formatter` provides the following +// enabled specializations: +// 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 +// +// This file tests with `ArithmeticT = unsigned integer`, for each valid `charT`. +// Where `unsigned integer` is one of: +// - unsigned char +// - unsigned short +// - unsigned +// - unsigned long +// - unsigned long long +// - __uint128_t + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +template +void test(StringT expected, StringViewT fmt, ArithmeticT arg) { + using CharT = typename StringT::value_type; + auto parse_ctx = std::basic_format_parse_context(fmt); + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + StringT result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + auto format_ctx = std::__format_context_create( + out, std::make_format_args(arg)); + formatter.format(arg, format_ctx); + assert(result == expected); +} + +template +void test_termination_condition(StringT expected, StringT f, ArithmeticT arg) { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + using CharT = typename StringT::value_type; + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt, arg); + fmt.remove_suffix(1); + test(expected, fmt, arg); +} + +template +void test_unsigned_integral_type() { + using A = ArithmeticT; + test_termination_condition(STR("0"), STR("}"), A(0)); + test_termination_condition(STR("255"), STR("}"), A(255)); + if (sizeof(A) > 1) + test_termination_condition(STR("65535"), STR("}"), A(65535)); + if (sizeof(A) > 2) + test_termination_condition(STR("4294967295"), STR("}"), A(4294967295)); + if (sizeof(A) > 4) + test_termination_condition(STR("8446744073709551615"), STR("}"), + A(8446744073709551615)); + + // TODO FMT Implement the __uint128_t maximum once the formatter can handle + // these values. +} + +template +void test_all_unsigned_integral_types() { + test_unsigned_integral_type(); + test_unsigned_integral_type(); + test_unsigned_integral_type(); + test_unsigned_integral_type(); + test_unsigned_integral_type(); +#ifndef _LIBCPP_HAS_NO_INT128 + test_unsigned_integral_type<__uint128_t, CharT>(); +#endif +} + +int main(int, char**) { + test_all_unsigned_integral_types(); + test_all_unsigned_integral_types(); + + 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,75 @@ +//===----------------------------------------------------------------------===// +// 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 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// template +// string format(const locale& loc, string_view fmt, const Args&... args); +// template +// wstring format(const locale& loc, wstring_view fmt, const Args&... args); + +#include +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto test = [](std::basic_string expected, + std::basic_string fmt, + const Args&... args) { + std::basic_string out = std::format(std::locale(), fmt, args...); + if constexpr (std::same_as) + if (out != expected) + std::cerr << "\nFormat string " << fmt << "\nExpected output " + << expected << "\nActual output " << out << '\n'; + assert(out == expected); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::format(std::locale(), fmt, args...); + if constexpr (std::same_as) + std::cerr << "\nFormat string " << fmt + << "\nDidn't throw an exception.\n"; + assert(false); + } catch (std::format_error& e) { +#ifdef _LIBCPP_VERSION + if constexpr (std::same_as) + if (e.what() != what) + std::cerr << "\nFormat string " << fmt << "\nExpected exception " + << what << "\nActual exception " << e.what() << '\n'; +#endif + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,83 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// Note this formatter shows additional information when tests are failing. +// This aids the development. Since other formatters fail in the same fashion +// they don't have this additional output. + +// + +// template +// string format(string_view fmt, const Args&... args); +// template +// wstring format(wstring_view fmt, const Args&... args); + +#include +#include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#include "test_macros.h" +#include "format_tests.h" + +auto test = [](std::basic_string expected, + std::basic_string fmt, + const Args&... args) { + std::basic_string out = std::format(fmt, args...); +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + if constexpr (std::same_as) + if (out != expected) + std::cerr << "\nFormat string " << fmt << "\nExpected output " + << expected << "\nActual output " << out << '\n'; +#endif + assert(out == expected); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::format(fmt, args...); +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + if constexpr (std::same_as) + std::cerr << "\nFormat string " << fmt + << "\nDidn't throw an exception.\n"; +#endif + assert(false); + } catch (std::format_error& e) { +#if defined(_LIBCPP_VERSION) && !defined(_LIBCPP_HAS_NO_LOCALIZATION) + if constexpr (std::same_as) + if (e.what() != what) + std::cerr << "\nFormat string " << fmt << "\nExpected exception " + << what << "\nActual exception " << e.what() << '\n'; +#endif + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h @@ -0,0 +1,144 @@ +//===----------------------------------------------------------------------===// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_STD_UTILITIES_FORMAT_FORMAT_FUNCTIONS_FORMAT_TESTS_H +#define TEST_STD_UTILITIES_FORMAT_FORMAT_FUNCTIONS_FORMAT_TESTS_H + +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) + +// TestFunction must be callable as check(expected-result, string-to-format, args-to-format...) +// ExceptionTest must be callable as check_exception(expected-exception, string-to-format, args-to-format...) +template +void format_tests(TestFunction check, ExceptionTest check_exception) { + // *** Test escaping *** + check(STR("{"), STR("{{")); + check(STR("}"), STR("}}")); + + // *** Test argument ID *** + check(STR("hello 01"), STR("hello {0:}{1:}"), false, true); + check(STR("hello 10"), STR("hello {1:}{0:}"), false, true); + + // ** Test invalid format strings *** + check_exception("The format string terminates at a '{'", STR("{")); + check_exception("The replacement field misses a terminating '}'", STR("{:"), + 42); + + check_exception("The format string contains an invalid escape sequence", + STR("}")); + check_exception("The format string contains an invalid escape sequence", + STR("{:}-}"), 42); + + check_exception("The format string contains an invalid escape sequence", + STR("} ")); + + check_exception( + "The arg-id of the format-spec starts with an invalid character", + STR("{-"), 42); + check_exception("Argument index out of bounds", STR("hello {}")); + check_exception("Argument index out of bounds", STR("hello {0}")); + check_exception("Argument index out of bounds", STR("hello {1}"), 42); + + // *** Test char format argument *** + // The `char` to `wchar_t` formatting is tested separately. + check(STR("hello 09azAZ!"), STR("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; + check(STR("hello 09azAZ!"), STR("hello {}"), data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + check(STR("hello 09azAZ!"), STR("hello {}"), data); + } + { + std::basic_string data = STR("world"); + check(STR("hello world"), STR("hello {}"), data); + } + { + std::basic_string buffer = STR("world"); + std::basic_string_view data = buffer; + check(STR("hello world"), STR("hello {}"), data); + } + + // *** Test Boolean format argument *** + check(STR("hello 01"), STR("hello {}{}"), false, true); + + // *** Test signed integral format argument *** + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + check(STR("hello 42"), STR("hello {}"), static_cast<__int128_t>(42)); + { + // Note 128-bit support is only partly implemented test the range + // conditions here. + std::basic_string min = + std::format(STR("{}"), std::numeric_limits::min()); + check(min, STR("{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = + std::format(STR("{}"), std::numeric_limits::max()); + check(max, STR("{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + check_exception( + "128-bit value is outside of implemented range", STR("{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + check_exception( + "128-bit value is outside of implemented range", STR("{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // ** Test unsigned integral format argument *** + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); + check(STR("hello 42"), STR("hello {}"), static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + check(STR("hello 42"), STR("hello {}"), static_cast<__uint128_t>(42)); + { + // Note 128-bit support is only partly implemented test the range + // conditions here. + std::basic_string max = + std::format(STR("{}"), std::numeric_limits::max()); + check(max, STR("{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + check_exception("128-bit value is outside of implemented range", STR("{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // *** Test floating point format argument *** +// TODO FMT Enable after floating-point support has been enabled +#if 0 + check(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); + check(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); + check(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); +#endif +} + +template +void format_tests_char_to_wchar_t(TestFunction check) { + using CharT = wchar_t; + check(STR("hello 09azA"), STR("hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +#endif 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,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 +// UNSUPPORTED: libcpp-has-no-localization +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto 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(), std::locale(), fmt, args...); + assert(it == out.end()); + assert(out == expected); + } + { + std::list out; + std::format_to(std::back_inserter(out), std::locale(), fmt, args...); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + std::vector out; + std::format_to(std::back_inserter(out), std::locale(), fmt, args...); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + assert(expected.size() < 4096 && "Update the size of the buffer."); + CharT out[4096]; + CharT* it = std::format_to(out, std::locale(), fmt, args...); + assert(std::distance(out, it) == int(expected.size())); + *it = '\0'; + assert(out == expected); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::basic_string out; + std::format_to(std::back_inserter(out), std::locale(), fmt, args...); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,91 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// 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 "format_tests.h" + +auto 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( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + std::vector out; + std::format_to(std::back_inserter(out), fmt, args...); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + assert(expected.size() < 4096 && "Update the size of the buffer."); + CharT out[4096]; + CharT* it = std::format_to(out, fmt, args...); + assert(std::distance(out, it) == int(expected.size())); + *it = '\0'; + assert(out == expected); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::basic_string out; + std::format_to(std::back_inserter(out), fmt, args...); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,128 @@ +//===----------------------------------------------------------------------===// +// 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 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto 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, std::locale(), 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(std::locale(), 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, std::locale(), fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(std::locale(), fmt, args...); + diff_type size = std::min(5, formatted_size); + + assert(result.size == formatted_size); + assert(std::equal(out.begin(), out.end(), expected.begin(), + expected.begin() + size)); + } + { + std::basic_string out; + std::format_to_n_result result = std::format_to_n( + std::back_inserter(out), 1000, std::locale(), fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(std::locale(), 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, std::locale(), fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(std::locale(), 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, std::locale(), fmt, args...); + using diff_type = decltype(result.size); + diff_type formatted_size = std::formatted_size(std::locale(), fmt, args...); + + assert(result.size == formatted_size); + assert(result.out == buffer); + assert(buffer[0] == CharT(0)); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::basic_string out; + std::format_to_n(std::back_inserter(out), 0, std::locale(), fmt, args...); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,124 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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 "format_tests.h" + +auto 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(std::equal(out.begin(), out.end(), expected.begin(), + expected.begin() + 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)); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_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) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,63 @@ +//===----------------------------------------------------------------------===// +// 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 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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); + +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto test = [](std::basic_string expected, + std::basic_string fmt, + const Args&... args) { + size_t size = std::formatted_size(std::locale(), fmt, args...); + assert(size == expected.size()); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::formatted_size(std::locale(), fmt, args...); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,59 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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 "format_tests.h" + +auto test = [](std::basic_string expected, + std::basic_string fmt, + const Args&... args) { + size_t size = std::formatted_size(fmt, args...); + assert(size == expected.size()); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::formatted_size(fmt, args...); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.functions/tests.inc b/libcxx/test/std/utilities/format/format.functions/tests.inc new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.functions/tests.inc @@ -0,0 +1,148 @@ +// TODO FMT DELETE THIS FILE +#if 0 +//===----------------------------------------------------------------------===// +// 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 +// +//===----------------------------------------------------------------------===// + +// Contains the test cases for the format functions without locale. + +#define STR(S) MAKE_STRING(CharT, S) + +void test_char_to_wchar() { + using CharT = wchar_t; + test(STR("hello 09azA"), STR("hello {}{}{}{}{}"), '0', '9', 'a', 'z', 'A'); +} + +template +void test() { + // *** Test escaping *** + test(STR("{"), STR("{{")); + test(STR("}"), STR("}}")); + + // *** Test argument ID *** + test(STR("hello 01"), STR("hello {0:}{1:}"), false, true); + test(STR("hello 10"), STR("hello {1:}{0:}"), false, true); + + // ** Test invalid format strings *** + test_exception("The format string terminates at a '{'", STR("{")); + test_exception("The replacement field misses a terminating '}'", STR("{:"), + 42); + + test_exception("The format string contains an invalid escape sequence", + STR("}")); + test_exception("The format string contains an invalid escape sequence", + STR("{:}-}"), 42); + + test_exception("The format string contains an invalid escape sequence", + STR("} ")); + + test_exception( + "The arg-id of the format-spec starts with an invalid character", + STR("{-"), 42); + test_exception("Argument index out of bounds", STR("hello {}")); + test_exception("Argument index out of bounds", STR("hello {0}")); + test_exception("Argument index out of bounds", STR("hello {1}"), 42); + + // *** Test char format argument *** + // The `char` to `wchar_t` formatting is tested separately. + test(STR("hello 09azAZ!"), STR("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(STR("hello 09azAZ!"), STR("hello {}"), data); + } + { + CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'), + CharT('A'), CharT('Z'), CharT('!'), 0}; + const CharT* data = buffer; + test(STR("hello 09azAZ!"), STR("hello {}"), data); + } + { + std::basic_string data = STR("world"); + test(STR("hello world"), STR("hello {}"), data); + } + { + std::basic_string buffer = STR("world"); + std::basic_string_view data = buffer; + test(STR("hello world"), STR("hello {}"), data); + } + + // *** Test Boolean format argument *** + test(STR("hello 01"), STR("hello {}{}"), false, true); + + // *** Test signed integral format argument *** + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(STR("hello 42"), STR("hello {}"), static_cast<__int128_t>(42)); + { + // Note 128-bit support is only partly implemented test the range + // conditions here. + std::basic_string min = + std::format(STR("{}"), std::numeric_limits::min()); + test(min, STR("{}"), + static_cast<__int128_t>(std::numeric_limits::min())); + std::basic_string max = + std::format(STR("{}"), std::numeric_limits::max()); + test(max, STR("{}"), + static_cast<__int128_t>(std::numeric_limits::max())); + test_exception( + "128-bit value is outside of implemented range", STR("{}"), + static_cast<__int128_t>(std::numeric_limits::min()) - 1); + test_exception( + "128-bit value is outside of implemented range", STR("{}"), + static_cast<__int128_t>(std::numeric_limits::max()) + 1); + } +#endif + + // ** Test unsigned integral format argument *** + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); + test(STR("hello 42"), STR("hello {}"), static_cast(42)); +#ifndef _LIBCPP_HAS_NO_INT128 + test(STR("hello 42"), STR("hello {}"), static_cast<__uint128_t>(42)); + { + // Note 128-bit support is only partly implemented test the range + // conditions here. + std::basic_string max = + std::format(STR("{}"), std::numeric_limits::max()); + test(max, STR("{}"), + static_cast<__uint128_t>( + std::numeric_limits::max())); + test_exception("128-bit value is outside of implemented range", STR("{}"), + static_cast<__uint128_t>( + std::numeric_limits::max()) + + 1); + } +#endif + + // *** Test floating point format argument *** +// TODO FMT Enable after floating-point support has been enabled +#if 0 + test(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); + test(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); + test(STR("hello 42.000000"), STR("hello {}"), static_cast(42)); +#endif +} + +int main(int, char**) { + test_char_to_wchar(); + + test(); + test(); + + return 0; +} +#endif 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,67 @@ +//===----------------------------------------------------------------------===// +// 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 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// string vformat(const locale& loc, string_view fmt, format_args args); +// wstring vformat(const locale& loc, wstring_view fmt, wformat_args args); + +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto test = [](std::basic_string expected, + std::basic_string fmt, + const Args&... args) { + std::basic_string out = std::vformat( + std::locale(), fmt, + std::make_format_args>, CharT>>( + args...)); + assert(out == expected); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::vformat( + std::locale(), fmt, + std::make_format_args>, CharT>>( + args...)); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,63 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// string vformat(string_view fmt, format_args args); +// wstring vformat(wstring_view fmt, wformat_args args); + +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto 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); +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::vformat( + fmt, std::make_format_args>, CharT>>( + args...)); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,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 +// UNSUPPORTED: libcpp-has-no-localization +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// 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); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "format_tests.h" + +auto 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(), std::locale(), 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), std::locale(), fmt, + std::make_format_args>, CharT>>(args...)); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + std::vector out; + std::vformat_to( + std::back_inserter(out), std::locale(), fmt, + std::make_format_args>, CharT>>(args...)); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + assert(expected.size() < 4096 && "Update the size of the buffer."); + CharT out[4096]; + CharT* it = std::vformat_to( + out, std::locale(), fmt, + std::make_format_args>( + args...)); + assert(std::distance(out, it) == int(expected.size())); + *it = '\0'; + assert(out == expected); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_NO_EXCEPTIONS + try { + std::basic_string out; + std::vformat_to( + std::back_inserter(out), std::locale(), fmt, + std::make_format_args>, CharT>>( + args...)); + assert(false); + } catch (std::format_error& e) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + 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,109 @@ +//===----------------------------------------------------------------------===// +// 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-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// 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 "format_tests.h" + +auto 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( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + std::vector out; + std::vformat_to( + std::back_inserter(out), fmt, + std::make_format_args>, CharT>>(args...)); + assert( + std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + } + { + assert(expected.size() < 4096 && "Update the size of the buffer."); + CharT out[4096]; + CharT* it = std::vformat_to( + out, fmt, + std::make_format_args>( + args...)); + assert(std::distance(out, it) == int(expected.size())); + *it = '\0'; + assert(out == expected); + } +}; + +auto test_exception = []( + std::string_view what, std::basic_string fmt, const Args&... args) { +#ifndef TEST_HAS_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) { + LIBCPP_ASSERT(e.what() == what); + return; + } + assert(false); +#else + (void)what; + (void)fmt; + (void)sizeof...(args); +#endif +}; + +int main(int, char**) { + format_tests_char_to_wchar_t(test); + + format_tests(test, test_exception); + format_tests(test, test_exception); + + return 0; +}