[libcxx-commits] [libcxx] 152d922 - [libc++][format] Improve floating-point formatters.
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 6 23:00:09 PDT 2022
Author: Mark de Wever
Date: 2022-07-07T08:00:05+02:00
New Revision: 152d92229580cdef4d70b61fff077b744aeb2994
URL: https://github.com/llvm/llvm-project/commit/152d92229580cdef4d70b61fff077b744aeb2994
DIFF: https://github.com/llvm/llvm-project/commit/152d92229580cdef4d70b61fff077b744aeb2994.diff
LOG: [libc++][format] Improve floating-point formatters.
This changes the implementation of the formatter. Instead of inheriting
from a specialized parser all formatters will use the same generic
parser. This reduces the binary size.
The new parser contains some additional fields only used in the chrono
formatting. Since this doesn't change the size of the parser the fields
are in the generic parser. The parser is designed to fit in 128-bit,
making it cheap to pass by value.
The new format function is a const member function. This isn't required
by the Standard yet, but it will be after LWG-3636 is accepted.
Additionally P2286 adds a formattable concept which requires the member
function to be const qualified in C++23. This paper is likely to be
accepted in the 2022 July plenary.
This is based on D125606. That commit did the groundwork and did similar
changes for the string formatters.
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D128785
Added:
Modified:
libcxx/include/__format/formatter_floating_point.h
libcxx/include/__format/formatter_output.h
libcxx/include/__format/parser_std_format_spec.h
Removed:
libcxx/test/libcxx/utilities/format/format.string/format.string.std/std_format_spec_floating_point.pass.cpp
################################################################################
diff --git a/libcxx/include/__format/formatter_floating_point.h b/libcxx/include/__format/formatter_floating_point.h
index c9f5689abd8b..9d8de95a7005 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 <charconv>
-#include <cmath>
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
# include <locale>
@@ -48,7 +46,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 17
-namespace __format_spec {
+namespace __formatter {
template <floating_point _Tp>
_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<char>{}.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 <class _Fp, class _Tp>
_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 <class _Fp, class _Tp>
+// 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 <class _OutIt, class _Fp, class _CharT>
-_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<numpunct<_CharT>>(__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
+ ptr
diff _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 <class _OutIt, class _CharT>
+_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 <floating_point _Tp>
- _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<same_as<_Tp, long double>, 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 <floating_point _Tp, class _CharT>
+_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
-
- ptr
diff _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<same_as<_Tp, long double>, 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 <class _OutIt>
- _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;
-
- ptr
diff _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
+
+ ptr
diff _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 <class _Fp, class _Tp>
- // 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 <floating_point _Tp>
+ _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<float, _CharT>
- : 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<double, _CharT>
- : 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<long double, _CharT>
- : 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 ab016f6f1610..f9f18a01c5bb 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 <class _CharT, class _ParserCharT>
+_LIBCPP_HIDE_FROM_ABI auto __write_using_trailing_zeros(
+ const _CharT* __first,
+ const _CharT* __last,
+ output_iterator<const _CharT&> 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 <class _CharT>
_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 739bdf457e40..de513b051a04 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 <class _CharT>
+_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
diff ers 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 ed57d54dddc6..000000000000
--- 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
-
-// <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 <format>
-#include <cassert>
-#ifndef _LIBCPP_HAS_NO_LOCALIZATION
-# include <iostream>
-#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 <class CharT>
-using Parser = __parser_floating_point<CharT>;
-
-template <class CharT>
-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 <class CharT>
-constexpr void test(Expected<CharT> expected, size_t size, std::basic_string_view<CharT> fmt) {
- // Initialize parser with sufficient arguments to avoid the parsing to fail
- // due to insufficient arguments.
- std::basic_format_parse_context<CharT> parse_ctx(fmt, std::__format::__number_max);
- auto begin = parse_ctx.begin();
- auto end = parse_ctx.end();
- Parser<CharT> 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 <class CharT>
-constexpr void test(Expected<CharT> 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<CharT> fmt{f};
- assert(fmt.back() == CharT('}') && "Pre-condition failure");
-
- test(expected, size, fmt);
- fmt.remove_suffix(1);
- test(expected, size, fmt);
-}
-
-template <class CharT>
-constexpr void test() {
- Parser<CharT> 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
diff er.
- // 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<Parser<CharT>>("The format-spec fill field contains an invalid character", CSTR("{<"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>("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<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR("2147483648"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR("5000000000"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR("10000000000"));
-
- test_exception<Parser<CharT>>("End of input while parsing format-spec arg-id", CSTR("{"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR("{0"));
- test_exception<Parser<CharT>>("The arg-id of the format-spec starts with an invalid character", CSTR("{a"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR("{1"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR("{9"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR("{9:"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR("{2147483648}"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR("{5000000000}"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>("The format-spec precision field doesn't contain a value or arg-id", CSTR(".a"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR(".2147483648"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR(".5000000000"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR(".10000000000"));
-
- test_exception<Parser<CharT>>("End of input while parsing format-spec arg-id", CSTR(".{"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR(".{0"));
- test_exception<Parser<CharT>>("The arg-id of the format-spec starts with an invalid character", CSTR(".{a"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR(".{1"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR(".{9"));
- test_exception<Parser<CharT>>("Invalid arg-id", CSTR(".{9:"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR(".{2147483648}"));
- test_exception<Parser<CharT>>("The numeric value of the format-spec is too large", CSTR(".{5000000000}"));
- test_exception<Parser<CharT>>("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<Parser<CharT>>(unsuported_type, CSTR("B}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("C}"));
- test_exception<Parser<CharT>>(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<Parser<CharT>>(not_a_type, CSTR("H}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("I}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("J}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("K}"));
- test({.locale_specific_form = true}, 1, CSTR("L}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("M}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("N}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("O}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("P}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("Q}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("R}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("S}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("T}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("U}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("V}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("W}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("X}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("Y}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("Z}"));
-
- test({.type = _Flags::_Type::__float_hexadecimal_lower_case}, 1, CSTR("a}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("b}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("c}"));
- test_exception<Parser<CharT>>(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<Parser<CharT>>(not_a_type, CSTR("h}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("i}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("j}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("k}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("l}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("m}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("n}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("o}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("p}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("q}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("r}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("s}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("t}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("u}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("v}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("w}"));
- test_exception<Parser<CharT>>(unsuported_type, CSTR("x}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("y}"));
- test_exception<Parser<CharT>>(not_a_type, CSTR("z}"));
- }
- // **** General ***
- test_exception<Parser<CharT>>("The format-spec should consume the input or end with a '}'", CSTR("ss"));
-}
-
-constexpr bool test() {
- test<char>();
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
- test<wchar_t>();
-#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<char>) == 3 * sizeof(uint32_t));
-# ifndef TEST_HAS_NO_WIDE_CHARACTERS
- static_assert(sizeof(Parser<wchar_t>) == (sizeof(wchar_t) <= 2 ? 3 * sizeof(uint32_t) : 4 * sizeof(uint32_t)));
-# endif
-#endif
-
- test();
- static_assert(test());
-
- return 0;
-}
More information about the libcxx-commits
mailing list