diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h index c9f5689abd8bd..9d8de95a70052 100644 --- a/libcxx/include/__format/formatter_floating_point.h +++ b/libcxx/include/__format/formatter_floating_point.h @@ -17,21 +17,19 @@ #include <__algorithm/min.h> #include <__algorithm/rotate.h> #include <__algorithm/transform.h> -#include <__assert> #include <__concepts/arithmetic.h> #include <__concepts/same_as.h> #include <__config> -#include <__format/format_error.h> #include <__format/format_fwd.h> -#include <__format/format_string.h> +#include <__format/format_parse_context.h> #include <__format/formatter.h> #include <__format/formatter_integral.h> +#include <__format/formatter_output.h> #include <__format/parser_std_format_spec.h> #include <__memory/allocator.h> #include <__utility/move.h> #include <__utility/unreachable.h> #include -#include #ifndef _LIBCPP_HAS_NO_LOCALIZATION # include @@ -48,7 +46,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER > 17 -namespace __format_spec { +namespace __formatter { template _LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last, _Tp __value) { @@ -164,7 +162,7 @@ class _LIBCPP_TEMPLATE_VIS __float_buffer { __precision_ = _Traits::__max_fractional; } - __size_ = __format_spec::__float_buffer_size<_Fp>(__precision_); + __size_ = __formatter::__float_buffer_size<_Fp>(__precision_); if (__size_ > _Traits::__stack_buffer_size) // The allocated buffer's contents don't need initialization. __begin_ = allocator{}.allocate(__size_); @@ -233,9 +231,9 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_default(const __float_buffe char* __integral) { __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value); - __result.__exponent = __format_spec::__find_exponent(__result.__integral, __result.__last); + __result.__exponent = __formatter::__find_exponent(__result.__integral, __result.__last); // Constrains: // - There's at least one decimal digit before the radix point. @@ -264,9 +262,9 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_hexadecimal_lower_case(cons __float_result __result; __result.__integral = __integral; if (__precision == -1) - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex); else - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::hex, __precision); // H = one or more hex-digits // S = sign @@ -315,7 +313,7 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_hexadecimal_upper_case(cons _Tp __value, int __precision, char* __integral) { __float_result __result = - __format_spec::__format_buffer_hexadecimal_lower_case(__buffer, __value, __precision, __integral); + __formatter::__format_buffer_hexadecimal_lower_case(__buffer, __value, __precision, __integral); _VSTD::transform(__result.__integral, __result.__exponent, __result.__integral, __hex_to_upper); *__result.__exponent = 'P'; return __result; @@ -328,13 +326,13 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_scientific_lower_case(const __float_result __result; __result.__integral = __integral; __result.__last = - __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::scientific, __precision); + __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::scientific, __precision); char* __first = __integral + 1; _LIBCPP_ASSERT(__first != __result.__last, "No exponent present"); if (*__first == '.') { __result.__radix_point = __first; - __result.__exponent = __format_spec::__find_exponent(__first + 1, __result.__last); + __result.__exponent = __formatter::__find_exponent(__first + 1, __result.__last); } else { __result.__radix_point = __result.__last; __result.__exponent = __first; @@ -354,7 +352,7 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_scientific_upper_case(const _Tp __value, int __precision, char* __integral) { __float_result __result = - __format_spec::__format_buffer_scientific_lower_case(__buffer, __value, __precision, __integral); + __formatter::__format_buffer_scientific_lower_case(__buffer, __value, __precision, __integral); *__result.__exponent = 'E'; return __result; } @@ -364,7 +362,7 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_fixed(const __float_buffer< int __precision, char* __integral) { __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::fixed, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::fixed, __precision); // When there's no precision there's no radix point. // Else the radix point is placed at __precision + 1 from the end. @@ -390,14 +388,14 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_general_lower_case(__float_ __float_result __result; __result.__integral = __integral; - __result.__last = __format_spec::__to_buffer(__integral, __buffer.end(), __value, chars_format::general, __precision); + __result.__last = __formatter::__to_buffer(__integral, __buffer.end(), __value, chars_format::general, __precision); char* __first = __integral + 1; if (__first == __result.__last) { __result.__radix_point = __result.__last; __result.__exponent = __result.__last; } else { - __result.__exponent = __format_spec::__find_exponent(__first, __result.__last); + __result.__exponent = __formatter::__find_exponent(__first, __result.__last); if (__result.__exponent != __result.__last) // In scientific mode if there's a radix point it will always be after // the first digit. (This is the position __first points at). @@ -423,19 +421,79 @@ _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_general_lower_case(__float_ template _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer_general_upper_case(__float_buffer<_Fp>& __buffer, _Tp __value, int __precision, char* __integral) { - __float_result __result = - __format_spec::__format_buffer_general_lower_case(__buffer, __value, __precision, __integral); + __float_result __result = __formatter::__format_buffer_general_lower_case(__buffer, __value, __precision, __integral); if (__result.__exponent != __result.__last) *__result.__exponent = 'E'; return __result; } -# ifndef _LIBCPP_HAS_NO_LOCALIZATION +/// Fills the buffer with the data based on the requested formatting. +/// +/// This function, when needed, turns the characters to upper case and +/// determines the "interesting" locations which are returned to the caller. +/// +/// This means the caller never has to convert the contents of the buffer to +/// upper case or search for radix points and the location of the exponent. +/// This gives a bit of overhead. The original code didn't do that, but due +/// to the number of possible additional work needed to turn this number to +/// the proper output the code was littered with tests for upper cases and +/// searches for radix points and exponents. +/// - When a precision larger than the type's precision is selected +/// additional zero characters need to be written before the exponent. +/// - alternate form needs to add a radix point when not present. +/// - localization needs to do grouping in the integral part. +template +// TODO FMT _Fp should just be _Tp when to_chars has proper long double support. +_LIBCPP_HIDE_FROM_ABI __float_result __format_buffer( + __float_buffer<_Fp>& __buffer, + _Tp __value, + bool __negative, + bool __has_precision, + __format_spec::__sign __sign, + __format_spec::__type __type) { + char* __first = __formatter::__insert_sign(__buffer.begin(), __negative, __sign); + switch (__type) { + case __format_spec::__type::__default: + return __formatter::__format_buffer_default(__buffer, __value, __first); + + case __format_spec::__type::__hexfloat_lower_case: + return __formatter::__format_buffer_hexadecimal_lower_case( + __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); + + case __format_spec::__type::__hexfloat_upper_case: + return __formatter::__format_buffer_hexadecimal_upper_case( + __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); + + case __format_spec::__type::__scientific_lower_case: + return __formatter::__format_buffer_scientific_lower_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__scientific_upper_case: + return __formatter::__format_buffer_scientific_upper_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__fixed_lower_case: + case __format_spec::__type::__fixed_upper_case: + return __formatter::__format_buffer_fixed(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__general_lower_case: + return __formatter::__format_buffer_general_lower_case(__buffer, __value, __buffer.__precision(), __first); + + case __format_spec::__type::__general_upper_case: + return __formatter::__format_buffer_general_upper_case(__buffer, __value, __buffer.__precision(), __first); + + default: + _LIBCPP_ASSERT(false, "The parser should have validated the type"); + __libcpp_unreachable(); + } +} + +# ifndef _LIBCPP_HAS_NO_LOCALIZATION template -_LIBCPP_HIDE_FROM_ABI _OutIt __format_locale_specific_form(_OutIt __out_it, const __float_buffer<_Fp>& __buffer, - const __float_result& __result, _VSTD::locale __loc, - size_t __width, _Flags::_Alignment __alignment, - _CharT __fill) { +_LIBCPP_HIDE_FROM_ABI _OutIt __format_locale_specific_form( + _OutIt __out_it, + const __float_buffer<_Fp>& __buffer, + const __float_result& __result, + _VSTD::locale __loc, + __format_spec::__parsed_specifications<_CharT> __specs) { const auto& __np = use_facet>(__loc); string __grouping = __np.grouping(); char* __first = __result.__integral; @@ -450,26 +508,27 @@ _LIBCPP_HIDE_FROM_ABI _OutIt __format_locale_specific_form(_OutIt __out_it, cons __grouping = __formatter::__determine_grouping(__digits, __grouping); } - size_t __size = __result.__last - __buffer.begin() + // Formatted string - __buffer.__num_trailing_zeros() + // Not yet rendered zeros - __grouping.size() - // Grouping contains one - !__grouping.empty(); // additional character + ptrdiff_t __size = + __result.__last - __buffer.begin() + // Formatted string + __buffer.__num_trailing_zeros() + // Not yet rendered zeros + __grouping.size() - // Grouping contains one + !__grouping.empty(); // additional character - __formatter::__padding_size_result __padding = {0, 0}; - bool __zero_padding = __alignment == _Flags::_Alignment::__default; - if (__size < __width) { + __formatter::__padding_size_result_v2 __padding = {0, 0}; + bool __zero_padding = __specs.__alignment_ == __format_spec::__alignment::__zero_padding; + if (__size < __specs.__width_) { if (__zero_padding) { - __alignment = _Flags::_Alignment::__right; - __fill = _CharT('0'); + __specs.__alignment_ = __format_spec::__alignment::__right; + __specs.__fill_ = _CharT('0'); } - __padding = __formatter::__padding_size(__size, __width, __alignment); + __padding = __formatter::__padding_size_v2(__size, __specs.__width_, __specs.__alignment_); } // sign and (zero padding or alignment) if (__zero_padding && __first != __buffer.begin()) *__out_it++ = *__buffer.begin(); - __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_); if (!__zero_padding && __first != __buffer.begin()) *__out_it++ = *__buffer.begin(); @@ -510,198 +569,148 @@ _LIBCPP_HIDE_FROM_ABI _OutIt __format_locale_specific_form(_OutIt __out_it, cons __out_it = _VSTD::copy(__result.__exponent, __result.__last, _VSTD::move(__out_it)); // alignment - return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_); +} +# endif // _LIBCPP_HAS_NO_LOCALIZATION + +template +_LIBCPP_HIDE_FROM_ABI _OutIt __format_floating_point_non_finite( + _OutIt __out_it, __format_spec::__parsed_specifications<_CharT> __specs, bool __negative, bool __isnan) { + char __buffer[4]; + char* __last = __formatter::__insert_sign(__buffer, __negative, __specs.__std_.__sign_); + + // to_chars can return inf, infinity, nan, and nan(n-char-sequence). + // The format library requires inf and nan. + // All in one expression to avoid dangling references. + bool __upper_case = + __specs.__std_.__type_ == __format_spec::__type::__hexfloat_upper_case || + __specs.__std_.__type_ == __format_spec::__type::__scientific_upper_case || + __specs.__std_.__type_ == __format_spec::__type::__fixed_upper_case || + __specs.__std_.__type_ == __format_spec::__type::__general_upper_case; + __last = _VSTD::copy_n(&("infnanINFNAN"[6 * __upper_case + 3 * __isnan]), 3, __last); + + // [format.string.std]/13 + // A zero (0) character preceding the width field pads the field with + // leading zeros (following any indication of sign or base) to the field + // width, except when applied to an infinity or NaN. + if (__specs.__alignment_ == __format_spec::__alignment::__zero_padding) + __specs.__alignment_ = __format_spec::__alignment::__right; + + return __formatter::__write(__buffer, __last, _VSTD::move(__out_it), __specs); } -# endif // _LIBCPP_HAS_NO_LOCALIZATION - -template <__formatter::__char_type _CharT> -class _LIBCPP_TEMPLATE_VIS __formatter_floating_point : public __parser_floating_point<_CharT> { -public: - template - _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) -> decltype(__ctx.out()) { - if (this->__width_needs_substitution()) - this->__substitute_width_arg_id(__ctx.arg(this->__width)); - - bool __negative = _VSTD::signbit(__value); - - if (!_VSTD::isfinite(__value)) [[unlikely]] - return __format_non_finite(__ctx.out(), __negative, _VSTD::isnan(__value)); - - bool __has_precision = this->__has_precision_field(); - if (this->__precision_needs_substitution()) - this->__substitute_precision_arg_id(__ctx.arg(this->__precision)); - - // Depending on the std-format-spec string the sign and the value - // might not be outputted together: - // - zero-padding may insert additional '0' characters. - // Therefore the value is processed as a non negative value. - // The function @ref __insert_sign will insert a '-' when the value was - // negative. - - if (__negative) - __value = _VSTD::copysign(__value, +1.0); - - // TODO FMT _Fp should just be _Tp when to_chars has proper long double support. - using _Fp = conditional_t, double, _Tp>; - // Force the type of the precision to avoid -1 to become an unsigned value. - __float_buffer<_Fp> __buffer(__has_precision ? int(this->__precision) : -1); - __float_result __result = __format_buffer(__buffer, __value, __negative, __has_precision); - - if (this->__alternate_form && __result.__radix_point == __result.__last) { - *__result.__last++ = '.'; - - // When there is an exponent the point needs to be moved before the - // exponent. When there's no exponent the rotate does nothing. Since - // rotate tests whether the operation is a nop, call it unconditionally. - _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); - __result.__radix_point = __result.__exponent; - - // The radix point is always placed before the exponent. - // - No exponent needs to point to the new last. - // - An exponent needs to move one position to the right. - // So it's safe to increment the value unconditionally. - ++__result.__exponent; - } +template +_LIBCPP_HIDE_FROM_ABI auto +__format_floating_point(_Tp __value, auto& __ctx, __format_spec::__parsed_specifications<_CharT> __specs) + -> decltype(__ctx.out()) { + bool __negative = _VSTD::signbit(__value); -# ifndef _LIBCPP_HAS_NO_LOCALIZATION - if (this->__locale_specific_form) - return __format_spec::__format_locale_specific_form(__ctx.out(), __buffer, __result, __ctx.locale(), - this->__width, this->__alignment, this->__fill); -# endif - - ptrdiff_t __size = __result.__last - __buffer.begin(); - int __num_trailing_zeros = __buffer.__num_trailing_zeros(); - if (__size + __num_trailing_zeros >= this->__width) { - if (__num_trailing_zeros && __result.__exponent != __result.__last) - // Insert trailing zeros before exponent character. - return _VSTD::copy(__result.__exponent, __result.__last, - _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__exponent, __ctx.out()), - __num_trailing_zeros, _CharT('0'))); - - return _VSTD::fill_n(_VSTD::copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, - _CharT('0')); - } + if (!_VSTD::isfinite(__value)) [[unlikely]] + return __formatter::__format_floating_point_non_finite(__ctx.out(), __specs, __negative, _VSTD::isnan(__value)); - auto __out_it = __ctx.out(); - char* __first = __buffer.begin(); - if (this->__alignment == _Flags::_Alignment::__default) { - // When there is a sign output it before the padding. Note the __size - // doesn't need any adjustment, regardless whether the sign is written - // here or in __formatter::__write. - if (__first != __result.__integral) - *__out_it++ = *__first++; - // After the sign is written, zero padding is the same a right alignment - // with '0'. - this->__alignment = _Flags::_Alignment::__right; - this->__fill = _CharT('0'); - } + // Depending on the std-format-spec string the sign and the value + // might not be outputted together: + // - zero-padding may insert additional '0' characters. + // Therefore the value is processed as a non negative value. + // The function @ref __insert_sign will insert a '-' when the value was + // negative. - if (__num_trailing_zeros) - return __formatter::__write(_VSTD::move(__out_it), __first, __result.__last, __size, this->__width, this->__fill, - this->__alignment, __result.__exponent, __num_trailing_zeros); + if (__negative) + __value = -__value; - return __formatter::__write(_VSTD::move(__out_it), __first, __result.__last, __size, this->__width, this->__fill, - this->__alignment); + // TODO FMT _Fp should just be _Tp when to_chars has proper long double support. + using _Fp = conditional_t, double, _Tp>; + // Force the type of the precision to avoid -1 to become an unsigned value. + __float_buffer<_Fp> __buffer(__specs.__precision_); + __float_result __result = __formatter::__format_buffer( + __buffer, __value, __negative, (__specs.__has_precision()), __specs.__std_.__sign_, __specs.__std_.__type_); + + if (__specs.__std_.__alternate_form_ && __result.__radix_point == __result.__last) { + *__result.__last++ = '.'; + + // When there is an exponent the point needs to be moved before the + // exponent. When there's no exponent the rotate does nothing. Since + // rotate tests whether the operation is a nop, call it unconditionally. + _VSTD::rotate(__result.__exponent, __result.__last - 1, __result.__last); + __result.__radix_point = __result.__exponent; + + // The radix point is always placed before the exponent. + // - No exponent needs to point to the new last. + // - An exponent needs to move one position to the right. + // So it's safe to increment the value unconditionally. + ++__result.__exponent; } -private: - template - _LIBCPP_HIDE_FROM_ABI _OutIt __format_non_finite(_OutIt __out_it, bool __negative, bool __isnan) { - char __buffer[4]; - char* __last = __insert_sign(__buffer, __negative, this->__sign); - - // to_char can return inf, infinity, nan, and nan(n-char-sequence). - // The format library requires inf and nan. - // All in one expression to avoid dangling references. - __last = _VSTD::copy_n(&("infnanINFNAN"[6 * (this->__type == _Flags::_Type::__float_hexadecimal_upper_case || - this->__type == _Flags::_Type::__scientific_upper_case || - this->__type == _Flags::_Type::__fixed_upper_case || - this->__type == _Flags::_Type::__general_upper_case) + - 3 * __isnan]), - 3, __last); - - // [format.string.std]/13 - // A zero (0) character preceding the width field pads the field with - // leading zeros (following any indication of sign or base) to the field - // width, except when applied to an infinity or NaN. - if (this->__alignment == _Flags::_Alignment::__default) - this->__alignment = _Flags::_Alignment::__right; - - ptrdiff_t __size = __last - __buffer; - if (__size >= this->__width) - return _VSTD::copy_n(__buffer, __size, _VSTD::move(__out_it)); - - return __formatter::__write(_VSTD::move(__out_it), __buffer, __last, __size, this->__width, this->__fill, - this->__alignment); +# ifndef _LIBCPP_HAS_NO_LOCALIZATION + if (__specs.__std_.__locale_specific_form_) + return __formatter::__format_locale_specific_form(__ctx.out(), __buffer, __result, __ctx.locale(), __specs); +# endif + + ptrdiff_t __size = __result.__last - __buffer.begin(); + int __num_trailing_zeros = __buffer.__num_trailing_zeros(); + if (__size + __num_trailing_zeros >= __specs.__width_) { + if (__num_trailing_zeros && __result.__exponent != __result.__last) + // Insert trailing zeros before exponent character. + return _VSTD::copy( + __result.__exponent, + __result.__last, + _VSTD::fill_n( + _VSTD::copy(__buffer.begin(), __result.__exponent, __ctx.out()), __num_trailing_zeros, _CharT('0'))); + + return _VSTD::fill_n( + _VSTD::copy(__buffer.begin(), __result.__last, __ctx.out()), __num_trailing_zeros, _CharT('0')); } - /// Fills the buffer with the data based on the requested formatting. - /// - /// This function, when needed, turns the characters to upper case and - /// determines the "interesting" locations which are returned to the caller. - /// - /// This means the caller never has to convert the contents of the buffer to - /// upper case or search for radix points and the location of the exponent. - /// This gives a bit of overhead. The original code didn't do that, but due - /// to the number of possible additional work needed to turn this number to - /// the proper output the code was littered with tests for upper cases and - /// searches for radix points and exponents. - /// - When a precision larger than the type's precision is selected - /// additional zero characters need to be written before the exponent. - /// - alternate form needs to add a radix point when not present. - /// - localization needs to do grouping in the integral part. - template - // TODO FMT _Fp should just be _Tp when to_chars has proper long double support. - _LIBCPP_HIDE_FROM_ABI __float_result __format_buffer(__float_buffer<_Fp>& __buffer, _Tp __value, bool __negative, - bool __has_precision) { - char* __first = __insert_sign(__buffer.begin(), __negative, this->__sign); - switch (this->__type) { - case _Flags::_Type::__default: - return __format_spec::__format_buffer_default(__buffer, __value, __first); - - case _Flags::_Type::__float_hexadecimal_lower_case: - return __format_spec::__format_buffer_hexadecimal_lower_case( - __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); - - case _Flags::_Type::__float_hexadecimal_upper_case: - return __format_spec::__format_buffer_hexadecimal_upper_case( - __buffer, __value, __has_precision ? __buffer.__precision() : -1, __first); - - case _Flags::_Type::__scientific_lower_case: - return __format_spec::__format_buffer_scientific_lower_case(__buffer, __value, __buffer.__precision(), __first); + auto __out_it = __ctx.out(); + char* __first = __buffer.begin(); + if (__specs.__alignment_ == __format_spec::__alignment ::__zero_padding) { + // When there is a sign output it before the padding. Note the __size + // doesn't need any adjustment, regardless whether the sign is written + // here or in __formatter::__write. + if (__first != __result.__integral) + *__out_it++ = *__first++; + // After the sign is written, zero padding is the same a right alignment + // with '0'. + __specs.__alignment_ = __format_spec::__alignment::__right; + __specs.__fill_ = _CharT('0'); + } - case _Flags::_Type::__scientific_upper_case: - return __format_spec::__format_buffer_scientific_upper_case(__buffer, __value, __buffer.__precision(), __first); + if (__num_trailing_zeros) + return __formatter::__write_using_trailing_zeros( + __first, __result.__last, _VSTD::move(__out_it), __specs, __size, __result.__exponent, __num_trailing_zeros); - case _Flags::_Type::__fixed_lower_case: - case _Flags::_Type::__fixed_upper_case: - return __format_spec::__format_buffer_fixed(__buffer, __value, __buffer.__precision(), __first); + return __formatter::__write(__first, __result.__last, _VSTD::move(__out_it), __specs, __size); +} - case _Flags::_Type::__general_lower_case: - return __format_spec::__format_buffer_general_lower_case(__buffer, __value, __buffer.__precision(), __first); +} // namespace __formatter - case _Flags::_Type::__general_upper_case: - return __format_spec::__format_buffer_general_upper_case(__buffer, __value, __buffer.__precision(), __first); +template <__formatter::__char_type _CharT> +struct _LIBCPP_TEMPLATE_VIS __formatter_floating_point { +public: + _LIBCPP_HIDE_FROM_ABI constexpr auto + parse(basic_format_parse_context<_CharT>& __parse_ctx) -> decltype(__parse_ctx.begin()) { + auto __result = __parser_.__parse(__parse_ctx, __format_spec::__fields_floating_point); + __format_spec::__process_parsed_floating_point(__parser_); + return __result; + } - default: - _LIBCPP_ASSERT(false, "The parser should have validated the type"); - __libcpp_unreachable(); - } + template + _LIBCPP_HIDE_FROM_ABI auto format(_Tp __value, auto& __ctx) const -> decltype(__ctx.out()) { + return __formatter::__format_floating_point(__value, __ctx, __parser_.__get_parsed_std_specifications(__ctx)); } -}; -} //namespace __format_spec + __format_spec::__parser<_CharT> __parser_; +}; template <__formatter::__char_type _CharT> struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; + : public __formatter_floating_point<_CharT> {}; template <__formatter::__char_type _CharT> struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; + : public __formatter_floating_point<_CharT> {}; template <__formatter::__char_type _CharT> struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter - : public __format_spec::__formatter_floating_point<_CharT> {}; + : public __formatter_floating_point<_CharT> {}; #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/formatter_output.h b/libcxx/include/__format/formatter_output.h index ab016f6f16107..f9f18a01c5bbe 100644 --- a/libcxx/include/__format/formatter_output.h +++ b/libcxx/include/__format/formatter_output.h @@ -222,6 +222,35 @@ _LIBCPP_HIDE_FROM_ABI auto __write_transformed(const _CharT* __first, const _Cha return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_); } +/// Writes additional zero's for the precision before the exponent. +/// This is used when the precision requested in the format string is larger +/// than the maximum precision of the floating-point type. These precision +/// digits are always 0. +/// +/// \param __exponent The location of the exponent character. +/// \param __num_trailing_zeros The number of 0's to write before the exponent +/// character. +template +_LIBCPP_HIDE_FROM_ABI auto __write_using_trailing_zeros( + const _CharT* __first, + const _CharT* __last, + output_iterator auto __out_it, + __format_spec::__parsed_specifications<_ParserCharT> __specs, + size_t __size, + const _CharT* __exponent, + size_t __num_trailing_zeros) -> decltype(__out_it) { + _LIBCPP_ASSERT(__first <= __last, "Not a valid range"); + _LIBCPP_ASSERT(__num_trailing_zeros > 0, "The overload not writing trailing zeros should have been used"); + + __padding_size_result_v2 __padding = + __padding_size_v2(__size + __num_trailing_zeros, __specs.__width_, __specs.__alignment_); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before_, __specs.__fill_); + __out_it = _VSTD::copy(__first, __exponent, _VSTD::move(__out_it)); + __out_it = _VSTD::fill_n(_VSTD::move(__out_it), __num_trailing_zeros, _CharT('0')); + __out_it = _VSTD::copy(__exponent, __last, _VSTD::move(__out_it)); + return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after_, __specs.__fill_); +} + # ifndef _LIBCPP_HAS_NO_UNICODE template _LIBCPP_HIDE_FROM_ABI auto __write_unicode_no_precision(basic_string_view<_CharT> __str, diff --git a/libcxx/include/__format/parser_std_format_spec.h b/libcxx/include/__format/parser_std_format_spec.h index 739bdf457e409..de513b051a043 100644 --- a/libcxx/include/__format/parser_std_format_spec.h +++ b/libcxx/include/__format/parser_std_format_spec.h @@ -1406,6 +1406,13 @@ inline constexpr __fields __fields_integral{ .__zero_padding_ = true, .__locale_specific_form_ = true, .__type_ = true}; +inline constexpr __fields __fields_floating_point{ + .__sign_ = true, + .__alternate_form_ = true, + .__zero_padding_ = true, + .__precision_ = true, + .__locale_specific_form_ = true, + .__type_ = true}; inline constexpr __fields __fields_string{.__precision_ = true, .__type_ = true}; inline constexpr __fields __fields_pointer{.__type_ = true}; @@ -1949,6 +1956,37 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_integer(__parser<_CharT>& } } +template +_LIBCPP_HIDE_FROM_ABI constexpr void __process_parsed_floating_point(__parser<_CharT>& __parser) { + __format_spec::__process_display_type_integer(__parser); + + switch (__parser.__type_) { + case __format_spec::__type::__default: + // When no precision specified then it keeps default since that + // formatting differs from the other types. + if (__parser.__precision_as_arg_ || __parser.__precision_ != -1) + __parser.__type_ = __format_spec::__type::__general_lower_case; + break; + case __format_spec::__type::__hexfloat_lower_case: + case __format_spec::__type::__hexfloat_upper_case: + // Precision specific behavior will be handled later. + break; + case __format_spec::__type::__scientific_lower_case: + case __format_spec::__type::__scientific_upper_case: + case __format_spec::__type::__fixed_lower_case: + case __format_spec::__type::__fixed_upper_case: + case __format_spec::__type::__general_lower_case: + case __format_spec::__type::__general_upper_case: + if (!__parser.__precision_as_arg_ && __parser.__precision_ == -1) + // Set the default precision for the call to to_chars. + __parser.__precision_ = 6; + break; + + default: + std::__throw_format_error("The format-spec type has a type not supported for a floating-point argument"); + } +} + _LIBCPP_HIDE_FROM_ABI constexpr void __process_display_type_pointer(__format_spec::__type __type) { switch (__type) { case __format_spec::__type::__default: diff --git a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp b/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp deleted file mode 100644 index ed57d54dddc61..0000000000000 --- a/libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp +++ /dev/null @@ -1,352 +0,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 -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: libcpp-has-no-incomplete-format - -// - -// Tests the parsing of the format string as specified in [format.string.std]. -// It validates whether the std-format-spec is valid for a floating-point type. - -#include -#include -#ifndef _LIBCPP_HAS_NO_LOCALIZATION -# include -#endif - -#include "concepts_precision.h" -#include "test_macros.h" -#include "make_string.h" -#include "test_exception.h" - -#define CSTR(S) MAKE_CSTRING(CharT, S) - -using namespace std::__format_spec; - -template -using Parser = __parser_floating_point; - -template -struct Expected { - CharT fill = CharT(' '); - _Flags::_Alignment alignment = _Flags::_Alignment::__right; - _Flags::_Sign sign = _Flags::_Sign::__default; - bool alternate_form = false; - bool zero_padding = false; - uint32_t width = 0; - bool width_as_arg = false; - uint32_t precision = std::__format::__number_max; - bool precision_as_arg = true; - bool locale_specific_form = false; - _Flags::_Type type = _Flags::_Type::__default; -}; - -template -constexpr void test(Expected expected, size_t size, std::basic_string_view fmt) { - // Initialize parser with sufficient arguments to avoid the parsing to fail - // due to insufficient arguments. - std::basic_format_parse_context parse_ctx(fmt, std::__format::__number_max); - auto begin = parse_ctx.begin(); - auto end = parse_ctx.end(); - Parser parser; - auto it = parser.parse(parse_ctx); - - assert(begin == parse_ctx.begin()); - assert(end == parse_ctx.end()); - - assert(begin + size == it); - assert(parser.__fill == expected.fill); - assert(parser.__alignment == expected.alignment); - assert(parser.__sign == expected.sign); - assert(parser.__alternate_form == expected.alternate_form); - assert(parser.__zero_padding == expected.zero_padding); - assert(parser.__width == expected.width); - assert(parser.__width_as_arg == expected.width_as_arg); - assert(parser.__precision == expected.precision); - assert(parser.__precision_as_arg == expected.precision_as_arg); - assert(parser.__locale_specific_form == expected.locale_specific_form); - assert(parser.__type == expected.type); -} - -template -constexpr void test(Expected expected, size_t size, const CharT* f) { - // 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, size, fmt); - fmt.remove_suffix(1); - test(expected, size, fmt); -} - -template -constexpr void test() { - Parser parser; - - assert(parser.__fill == CharT(' ')); - assert(parser.__alignment == _Flags::_Alignment::__default); - assert(parser.__sign == _Flags::_Sign::__default); - assert(parser.__alternate_form == false); - assert(parser.__zero_padding == false); - assert(parser.__width == 0); - assert(parser.__width_as_arg == false); - assert(parser.__precision == std::__format::__number_max); - assert(parser.__precision_as_arg == true); - assert(parser.__locale_specific_form == false); - assert(parser.__type == _Flags::_Type::__default); - - // Depending on whether or not a precision is specified the results differ. - // Table 65: Meaning of type options for floating-point types [tab:format.type.float] - - test({}, 0, CSTR("}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".0}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{1}}")); - - test({.type = _Flags::_Type::__float_hexadecimal_lower_case}, 1, CSTR("a}")); - test({.type = _Flags::_Type::__float_hexadecimal_upper_case}, 1, CSTR("A}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 1, CSTR("e}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 3, CSTR(".0e}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__scientific_lower_case}, 5, CSTR(".{1}e}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 1, CSTR("E}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 3, CSTR(".0E}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__scientific_upper_case}, 5, CSTR(".{1}E}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 1, CSTR("f}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 3, CSTR(".0f}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__fixed_lower_case}, 5, CSTR(".{1}f}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 1, CSTR("F}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 3, CSTR(".0F}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__fixed_upper_case}, 5, CSTR(".{1}F}")); - - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 1, CSTR("g}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".0g}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 5, CSTR(".{1}g}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 1, CSTR("G}")); - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 3, CSTR(".0G}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_upper_case}, 5, CSTR(".{1}G}")); - - // *** Align-fill *** - test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}")); - test({.alignment = _Flags::_Alignment::__center}, 1, "^}"); - test({.alignment = _Flags::_Alignment::__right}, 1, ">}"); - - test({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 2, CSTR("L<}")); - test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 2, CSTR("#^}")); - test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 2, CSTR("0>}")); - - test_exception>("The format-spec fill field contains an invalid character", CSTR("{<")); - test_exception>("The format-spec fill field contains an invalid character", CSTR("}<")); - - // *** Sign *** - test({.sign = _Flags::_Sign::__minus}, 1, CSTR("-}")); - test({.sign = _Flags::_Sign::__plus}, 1, CSTR("+}")); - test({.sign = _Flags::_Sign::__space}, 1, CSTR(" }")); - - // *** Alternate form *** - test({.alternate_form = true}, 1, CSTR("#}")); - - // *** Zero padding *** - // TODO FMT What to do with zero-padding without a width? - // [format.string.std]/13 - // A zero (0) character preceding the width field pads the field with - // leading zeros (following any indication of sign or base) to the field - // width, except when applied to an infinity or NaN. - // Obviously it makes no sense, but should it be allowed or is it a format - // error? - test({.alignment = _Flags::_Alignment::__default, .zero_padding = true}, 1, CSTR("0}")); - test({.alignment = _Flags::_Alignment::__left, .zero_padding = false}, 2, CSTR("<0}")); - test({.alignment = _Flags::_Alignment::__center, .zero_padding = false}, 2, CSTR("^0}")); - test({.alignment = _Flags::_Alignment::__right, .zero_padding = false}, 2, CSTR(">0}")); - - // *** Width *** - test({.width = 0, .width_as_arg = false}, 0, CSTR("}")); - test({.width = 1, .width_as_arg = false}, 1, CSTR("1}")); - test({.width = 10, .width_as_arg = false}, 2, CSTR("10}")); - test({.width = 1000, .width_as_arg = false}, 4, CSTR("1000}")); - test({.width = 1000000, .width_as_arg = false}, 7, CSTR("1000000}")); - - test({.width = 0, .width_as_arg = true}, 2, CSTR("{}}")); - test({.width = 0, .width_as_arg = true}, 3, CSTR("{0}}")); - test({.width = 1, .width_as_arg = true}, 3, CSTR("{1}}")); - - test_exception>("A format-spec width field shouldn't have a leading zero", CSTR("00")); - - static_assert(std::__format::__number_max == 2'147'483'647, "Update the assert and the test."); - test({.width = 2'147'483'647, .width_as_arg = false}, 10, CSTR("2147483647}")); - test_exception>("The numeric value of the format-spec is too large", CSTR("2147483648")); - test_exception>("The numeric value of the format-spec is too large", CSTR("5000000000")); - test_exception>("The numeric value of the format-spec is too large", CSTR("10000000000")); - - test_exception>("End of input while parsing format-spec arg-id", CSTR("{")); - test_exception>("Invalid arg-id", CSTR("{0")); - test_exception>("The arg-id of the format-spec starts with an invalid character", CSTR("{a")); - test_exception>("Invalid arg-id", CSTR("{1")); - test_exception>("Invalid arg-id", CSTR("{9")); - test_exception>("Invalid arg-id", CSTR("{9:")); - test_exception>("Invalid arg-id", CSTR("{9a")); - static_assert(std::__format::__number_max == 2'147'483'647, "Update the assert and the test."); - // Note the static_assert tests whether the arg-id is valid. - // Therefore the following should be true arg-id < __format::__number_max. - test({.width = 2'147'483'646, .width_as_arg = true}, 12, CSTR("{2147483646}}")); - test_exception>("The numeric value of the format-spec is too large", CSTR("{2147483648}")); - test_exception>("The numeric value of the format-spec is too large", CSTR("{5000000000}")); - test_exception>("The numeric value of the format-spec is too large", CSTR("{10000000000}")); - - // *** Precision *** - test({.precision = 0, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".0}")); - test({.precision = 1, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 2, CSTR(".1}")); - test({.precision = 10, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".10}")); - test({.precision = 1000, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 5, CSTR(".1000}")); - test({.precision = 1000000, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 8, - CSTR(".1000000}")); - - test({.precision = 0, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 3, CSTR(".{}}")); - test({.precision = 0, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{0}}")); - test({.precision = 1, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 4, CSTR(".{1}}")); - - test_exception>("The format-spec precision field doesn't contain a value or arg-id", CSTR(".a")); - test_exception>("The format-spec precision field doesn't contain a value or arg-id", CSTR(".:")); - - static_assert(std::__format::__number_max == 2'147'483'647, "Update the assert and the test."); - test({.precision = 2'147'483'647, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 11, - CSTR(".2147483647}")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".2147483648")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".5000000000")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".10000000000")); - - test_exception>("End of input while parsing format-spec arg-id", CSTR(".{")); - test_exception>("Invalid arg-id", CSTR(".{0")); - test_exception>("The arg-id of the format-spec starts with an invalid character", CSTR(".{a")); - test_exception>("Invalid arg-id", CSTR(".{1")); - test_exception>("Invalid arg-id", CSTR(".{9")); - test_exception>("Invalid arg-id", CSTR(".{9:")); - test_exception>("Invalid arg-id", CSTR(".{9a")); - - static_assert(std::__format::__number_max == 2'147'483'647, "Update the assert and the test."); - // Note the static_assert tests whether the arg-id is valid. - // Therefore the following should be true arg-id < __format::__number_max. - test({.precision = 2'147'483'646, .precision_as_arg = true, .type = _Flags::_Type::__general_lower_case}, 13, - CSTR(".{2147483646}}")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".{2147483648}")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".{5000000000}")); - test_exception>("The numeric value of the format-spec is too large", CSTR(".{10000000000}")); - - // *** Width & Precision *** - test({.width = 1, - .width_as_arg = false, - .precision = 0, - .precision_as_arg = false, - .type = _Flags::_Type::__general_lower_case}, - 3, CSTR("1.0}")); - test({.width = 0, - .width_as_arg = true, - .precision = 1, - .precision_as_arg = true, - .type = _Flags::_Type::__general_lower_case}, - 5, CSTR("{}.{}}")); - test({.width = 10, - .width_as_arg = true, - .precision = 9, - .precision_as_arg = true, - .type = _Flags::_Type::__general_lower_case}, - 8, CSTR("{10}.{9}}")); - - // *** Locale-specific form *** - test({.locale_specific_form = true}, 1, CSTR("L}")); - - // *** Type *** - { - const char* unsuported_type = "The format-spec type has a type not supported for a floating-point argument"; - const char* not_a_type = "The format-spec should consume the input or end with a '}'"; - - test({.type = _Flags::_Type::__float_hexadecimal_upper_case}, 1, CSTR("A}")); - test_exception>(unsuported_type, CSTR("B}")); - test_exception>(not_a_type, CSTR("C}")); - test_exception>(not_a_type, CSTR("D}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_upper_case}, 1, CSTR("E}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_upper_case}, 1, CSTR("F}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_upper_case}, 1, CSTR("G}")); - test_exception>(not_a_type, CSTR("H}")); - test_exception>(not_a_type, CSTR("I}")); - test_exception>(not_a_type, CSTR("J}")); - test_exception>(not_a_type, CSTR("K}")); - test({.locale_specific_form = true}, 1, CSTR("L}")); - test_exception>(not_a_type, CSTR("M}")); - test_exception>(not_a_type, CSTR("N}")); - test_exception>(not_a_type, CSTR("O}")); - test_exception>(not_a_type, CSTR("P}")); - test_exception>(not_a_type, CSTR("Q}")); - test_exception>(not_a_type, CSTR("R}")); - test_exception>(not_a_type, CSTR("S}")); - test_exception>(not_a_type, CSTR("T}")); - test_exception>(not_a_type, CSTR("U}")); - test_exception>(not_a_type, CSTR("V}")); - test_exception>(not_a_type, CSTR("W}")); - test_exception>(unsuported_type, CSTR("X}")); - test_exception>(not_a_type, CSTR("Y}")); - test_exception>(not_a_type, CSTR("Z}")); - - test({.type = _Flags::_Type::__float_hexadecimal_lower_case}, 1, CSTR("a}")); - test_exception>(unsuported_type, CSTR("b}")); - test_exception>(unsuported_type, CSTR("c}")); - test_exception>(unsuported_type, CSTR("d}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__scientific_lower_case}, 1, CSTR("e}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__fixed_lower_case}, 1, CSTR("f}")); - test({.precision = 6, .precision_as_arg = false, .type = _Flags::_Type::__general_lower_case}, 1, CSTR("g}")); - test_exception>(not_a_type, CSTR("h}")); - test_exception>(not_a_type, CSTR("i}")); - test_exception>(not_a_type, CSTR("j}")); - test_exception>(not_a_type, CSTR("k}")); - test_exception>(not_a_type, CSTR("l}")); - test_exception>(not_a_type, CSTR("m}")); - test_exception>(not_a_type, CSTR("n}")); - test_exception>(unsuported_type, CSTR("o}")); - test_exception>(unsuported_type, CSTR("p}")); - test_exception>(not_a_type, CSTR("q}")); - test_exception>(not_a_type, CSTR("r}")); - test_exception>(unsuported_type, CSTR("s}")); - test_exception>(not_a_type, CSTR("t}")); - test_exception>(not_a_type, CSTR("u}")); - test_exception>(not_a_type, CSTR("v}")); - test_exception>(not_a_type, CSTR("w}")); - test_exception>(unsuported_type, CSTR("x}")); - test_exception>(not_a_type, CSTR("y}")); - test_exception>(not_a_type, CSTR("z}")); - } - // **** General *** - test_exception>("The format-spec should consume the input or end with a '}'", CSTR("ss")); -} - -constexpr bool test() { - test(); -#ifndef TEST_HAS_NO_WIDE_CHARACTERS - test(); -#endif - - return true; -} - -int main(int, char**) { -#if !defined(_WIN32) && !defined(_AIX) - // Make sure the parsers match the expectations. The layout of the - // subobjects is chosen to minimize the size required. - static_assert(sizeof(Parser) == 3 * sizeof(uint32_t)); -# ifndef TEST_HAS_NO_WIDE_CHARACTERS - static_assert(sizeof(Parser) == (sizeof(wchar_t) <= 2 ? 3 * sizeof(uint32_t) : 4 * sizeof(uint32_t))); -# endif -#endif - - test(); - static_assert(test()); - - return 0; -}