[libc++][format] Adds integer formatter.
Implements the formatter for all fundamental integer types (except `char`, `wchar_t`, and `bool`). [format.formatter.spec]/2.3 For each charT, for each cv-unqualified arithmetic type ArithmeticT other than char, wchar_t, char8_t, char16_t, or char32_t, a specialization ``` template<> struct formatter<ArithmeticT, charT>; ``` This removes the stub implemented in D96664. As an extension it adds partial support for 128-bit integer types. Implements parts of: - P0645 Text Formatting - P1652 Printf corner cases in std::format Completes: - LWG-3248 #b, #B, #o, #x, and #X presentation types misformat negative numbers Reviewed By: #libc, ldionne, vitaut Differential Revision: https://reviews.llvm.org/D103433
This commit is contained in:
@@ -205,7 +205,7 @@
|
||||
"`3242 <https://wg21.link/LWG3242>`__","``std::format``\ : missing rules for ``arg-id``\ in ``width``\ and ``precision``\ ","Prague","|Complete|","Clang 14"
|
||||
"`3243 <https://wg21.link/LWG3243>`__","``std::format``\ and negative zeroes","Prague","",""
|
||||
"`3247 <https://wg21.link/LWG3247>`__","``ranges::iter_move``\ should perform ADL-only lookup of ``iter_move``\ ","Prague","",""
|
||||
"`3248 <https://wg21.link/LWG3248>`__","``std::format``\ ``#b``\ , ``#B``\ , ``#o``\ , ``#x``\ , and ``#X``\ presentation types misformat negative numbers","Prague","",""
|
||||
"`3248 <https://wg21.link/LWG3248>`__","``std::format``\ ``#b``\ , ``#B``\ , ``#o``\ , ``#x``\ , and ``#X``\ presentation types misformat negative numbers","Prague","|Complete|","14.0"
|
||||
"`3250 <https://wg21.link/LWG3250>`__","``std::format``\ : ``#``\ (alternate form) for NaN and inf","Prague","",""
|
||||
"`3251 <https://wg21.link/LWG3251>`__","Are ``std::format``\ alignment specifiers applied to string arguments?","Prague","",""
|
||||
"`3252 <https://wg21.link/LWG3252>`__","Parse locale's aware modifiers for commands are not consistent with POSIX spec","Prague","",""
|
||||
|
||||
|
@@ -129,7 +129,7 @@
|
||||
"`P1644 <https://wg21.link/P1644>`__","LWG","Add wait/notify to atomic<shared_ptr>","Cologne","",""
|
||||
"`P1650 <https://wg21.link/P1650>`__","LWG","Output std::chrono::days with 'd' suffix","Cologne","",""
|
||||
"`P1651 <https://wg21.link/P1651>`__","LWG","bind_front should not unwrap reference_wrapper","Cologne","|Complete|","13.0"
|
||||
"`P1652 <https://wg21.link/P1652>`__","LWG","Printf corner cases in std::format","Cologne","",""
|
||||
"`P1652 <https://wg21.link/P1652>`__","LWG","Printf corner cases in std::format","Cologne","|In Progress|",""
|
||||
"`P1661 <https://wg21.link/P1661>`__","LWG","Remove dedicated precalculated hash lookup interface","Cologne","|Nothing To Do|",""
|
||||
"`P1754 <https://wg21.link/P1754>`__","LWG","Rename concepts to standard_case for C++20, while we still can","Cologne","|In Progress|",""
|
||||
"","","","","",""
|
||||
|
||||
|
@@ -140,6 +140,8 @@ set(files
|
||||
__format/format_parse_context.h
|
||||
__format/format_string.h
|
||||
__format/formatter.h
|
||||
__format/formatter_integer.h
|
||||
__format/formatter_integral.h
|
||||
__format/formatter_string.h
|
||||
__format/parser_std_format_spec.h
|
||||
__function_like.h
|
||||
|
||||
@@ -147,6 +147,30 @@ __write(output_iterator<const _CharT&> auto __out_it, const _CharT* __first,
|
||||
return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill);
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
*
|
||||
* Uses a transformation operation before writing an element.
|
||||
*
|
||||
* TODO FMT Fill will probably change to support Unicode grapheme cluster.
|
||||
*/
|
||||
template <class _CharT, class _UnaryOperation, class _Fill>
|
||||
_LIBCPP_HIDE_FROM_ABI auto
|
||||
__write(output_iterator<const _CharT&> auto __out_it, const _CharT* __first,
|
||||
const _CharT* __last, size_t __size, _UnaryOperation __op,
|
||||
size_t __width, _Fill __fill,
|
||||
__format_spec::_Flags::_Alignment __alignment) -> decltype(__out_it) {
|
||||
|
||||
_LIBCPP_ASSERT(__first <= __last, "Not a valid range");
|
||||
_LIBCPP_ASSERT(__size < __width, "Precondition failure");
|
||||
|
||||
__padding_size_result __padding =
|
||||
__padding_size(__size, __width, __alignment);
|
||||
__out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before, __fill);
|
||||
__out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it), __op);
|
||||
return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after, __fill);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes Unicode input to the output with the required padding.
|
||||
*
|
||||
|
||||
170
libcxx/include/__format/formatter_integer.h
Normal file
170
libcxx/include/__format/formatter_integer.h
Normal file
@@ -0,0 +1,170 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FORMAT_FORMATTER_INTEGER_H
|
||||
#define _LIBCPP___FORMAT_FORMATTER_INTEGER_H
|
||||
|
||||
#include <__availability>
|
||||
#include <__config>
|
||||
#include <__format/format_error.h>
|
||||
#include <__format/format_fwd.h>
|
||||
#include <__format/formatter.h>
|
||||
#include <__format/formatter_integral.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
#include <limits>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_PUSH_MACROS
|
||||
#include <__undef_macros>
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
// TODO FMT Remove this once we require compilers with proper C++20 support.
|
||||
// If the compiler has no concepts support, the format header will be disabled.
|
||||
// Without concepts support enable_if needs to be used and that too much effort
|
||||
// to support compilers with partial C++20 support.
|
||||
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
namespace __format_spec {
|
||||
|
||||
template <class _CharT>
|
||||
class _LIBCPP_TEMPLATE_VIS __parser_integer : public __parser_integral<_CharT> {
|
||||
public:
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx)
|
||||
-> decltype(__parse_ctx.begin()) {
|
||||
auto __it = __parser_integral<_CharT>::__parse(__parse_ctx);
|
||||
|
||||
switch (this->__type) {
|
||||
case _Flags::_Type::__default:
|
||||
this->__type = _Flags::_Type::__decimal;
|
||||
[[fallthrough]];
|
||||
|
||||
case _Flags::_Type::__binary_lower_case:
|
||||
case _Flags::_Type::__binary_upper_case:
|
||||
case _Flags::_Type::__octal:
|
||||
case _Flags::_Type::__decimal:
|
||||
case _Flags::_Type::__hexadecimal_lower_case:
|
||||
case _Flags::_Type::__hexadecimal_upper_case:
|
||||
this->__handle_integer();
|
||||
break;
|
||||
|
||||
case _Flags::_Type::__char:
|
||||
this->__handle_char();
|
||||
break;
|
||||
|
||||
default:
|
||||
__throw_format_error("The format-spec type has a type not supported for "
|
||||
"an integer argument");
|
||||
}
|
||||
return __it;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _CharT>
|
||||
using __formatter_integer = __formatter_integral<__parser_integer<_CharT>>;
|
||||
|
||||
} // namespace __format_spec
|
||||
|
||||
// [format.formatter.spec]/2.3
|
||||
// For each charT, for each cv-unqualified arithmetic type ArithmeticT other
|
||||
// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization
|
||||
|
||||
// Signed integral types.
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<signed char, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<short, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<int, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<long, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<long long, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<__int128_t, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {
|
||||
using _Base = __format_spec::__formatter_integer<_CharT>;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI auto format(__int128_t __value, auto& __ctx)
|
||||
-> decltype(__ctx.out()) {
|
||||
// TODO FMT Implement full 128 bit support.
|
||||
using _To = long long;
|
||||
if (__value < numeric_limits<_To>::min() ||
|
||||
__value > numeric_limits<_To>::max())
|
||||
__throw_format_error("128-bit value is outside of implemented range");
|
||||
|
||||
return _Base::format(static_cast<_To>(__value), __ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Unsigned integral types.
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<unsigned char, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<unsigned short, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<unsigned, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<unsigned long, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<unsigned long long, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {};
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<__uint128_t, _CharT>
|
||||
: public __format_spec::__formatter_integer<_CharT> {
|
||||
using _Base = __format_spec::__formatter_integer<_CharT>;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI auto format(__uint128_t __value, auto& __ctx)
|
||||
-> decltype(__ctx.out()) {
|
||||
// TODO FMT Implement full 128 bit support.
|
||||
using _To = unsigned long long;
|
||||
if (__value < numeric_limits<_To>::min() ||
|
||||
__value > numeric_limits<_To>::max())
|
||||
__throw_format_error("128-bit value is outside of implemented range");
|
||||
|
||||
return _Base::format(static_cast<_To>(__value), __ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
#endif //_LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___FORMAT_FORMATTER_INTEGER_H
|
||||
502
libcxx/include/__format/formatter_integral.h
Normal file
502
libcxx/include/__format/formatter_integral.h
Normal file
@@ -0,0 +1,502 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H
|
||||
#define _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H
|
||||
|
||||
#include <__config>
|
||||
#include <__format/format_error.h>
|
||||
#include <__format/format_fwd.h>
|
||||
#include <__format/formatter.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
#include <__algorithm/copy.h>
|
||||
#include <__algorithm/copy_n.h>
|
||||
#include <__algorithm/fill_n.h>
|
||||
#include <__algorithm/transform.h>
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
||||
#include <locale>
|
||||
#endif
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_PUSH_MACROS
|
||||
#include <__undef_macros>
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
// TODO FMT Remove this once we require compilers with proper C++20 support.
|
||||
// If the compiler has no concepts support, the format header will be disabled.
|
||||
// Without concepts support enable_if needs to be used and that too much effort
|
||||
// to support compilers with partial C++20 support.
|
||||
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
/**
|
||||
* Integral formatting classes.
|
||||
*
|
||||
* There are two types used here:
|
||||
* * C++-type, the type as used in C++.
|
||||
* * format-type, the output type specified in the std-format-spec.
|
||||
*
|
||||
* Design of the integral formatters consists of several layers.
|
||||
* * @ref __parser_integral The basic std-format-spec parser for all integral
|
||||
* classes. This parser does the basic sanity checks. It also contains some
|
||||
* helper functions that are nice to have available for all parsers.
|
||||
* * A C++-type specific parser. These parsers must derive from
|
||||
* @ref __parser_integral. Their task is to validate whether the parsed
|
||||
* std-format-spec is valid for the C++-type and selected format-type. After
|
||||
* validation they need to make sure all members are properly set. For
|
||||
* example, when the alignment hasn't changed it needs to set the proper
|
||||
* default alignment for the format-type. The following parsers are available:
|
||||
* - @ref __parser_integer
|
||||
* - @ref __parser_char
|
||||
* - @ref __parser_bool
|
||||
* * A general formatter for all integral types @ref __formatter_integral. This
|
||||
* formatter can handle all formatting of integers and characters. The class
|
||||
* derives from the proper formatter.
|
||||
* Note the boolean string format-type isn't supported in this class.
|
||||
* * A typedef C++-type group combining the @ref __formatter_integral with a
|
||||
* parser:
|
||||
* * @ref __formatter_integer
|
||||
* * @ref __formatter_char
|
||||
* * @ref __formatter_bool
|
||||
* * Then every C++-type has its own formatter specializations. They inherit
|
||||
* from the C++-type group typedef. Most specializations need nothing else.
|
||||
* Others need some additional specializations in this class.
|
||||
*/
|
||||
namespace __format_spec {
|
||||
|
||||
/** Wrapper around @ref to_chars, returning the output pointer. */
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI char* __to_buffer(char* __first, char* __last,
|
||||
_Tp __value, int __base) {
|
||||
// TODO FMT Evaluate code overhead due to not calling the internal function
|
||||
// directly. (Should be zero overhead.)
|
||||
to_chars_result __r = _VSTD::to_chars(__first, __last, __value, __base);
|
||||
_LIBCPP_ASSERT(__r.ec == errc(0), "Internal buffer too small");
|
||||
return __r.ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to determine the buffer size to output a integer in Base @em x.
|
||||
*
|
||||
* There are several overloads for the supported bases. The function uses the
|
||||
* base as template argument so it can be used in a constant expression.
|
||||
*/
|
||||
template <unsigned_integral _Tp, size_t _Base>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept
|
||||
requires(_Base == 2) {
|
||||
return numeric_limits<_Tp>::digits // The number of binary digits.
|
||||
+ 2 // Reserve space for the '0[Bb]' prefix.
|
||||
+ 1; // Reserve space for the sign.
|
||||
}
|
||||
|
||||
template <unsigned_integral _Tp, size_t _Base>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept
|
||||
requires(_Base == 8) {
|
||||
return numeric_limits<_Tp>::digits // The number of binary digits.
|
||||
/ 3 // Adjust to octal.
|
||||
+ 1 // Turn floor to ceil.
|
||||
+ 1 // Reserve space for the '0' prefix.
|
||||
+ 1; // Reserve space for the sign.
|
||||
}
|
||||
|
||||
template <unsigned_integral _Tp, size_t _Base>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept
|
||||
requires(_Base == 10) {
|
||||
return numeric_limits<_Tp>::digits10 // The floored value.
|
||||
+ 1 // Turn floor to ceil.
|
||||
+ 1; // Reserve space for the sign.
|
||||
}
|
||||
|
||||
template <unsigned_integral _Tp, size_t _Base>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr size_t __buffer_size() noexcept
|
||||
requires(_Base == 16) {
|
||||
return numeric_limits<_Tp>::digits // The number of binary digits.
|
||||
/ 4 // Adjust to hexadecimal.
|
||||
+ 2 // Reserve space for the '0[Xx]' prefix.
|
||||
+ 1; // Reserve space for the sign.
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI inline char* __insert_sign(char* __buf, bool __negative,
|
||||
_Flags::_Sign __sign) {
|
||||
if (__negative)
|
||||
*__buf++ = '-';
|
||||
else
|
||||
switch (__sign) {
|
||||
case _Flags::_Sign::__default:
|
||||
case _Flags::_Sign::__minus:
|
||||
// No sign added.
|
||||
break;
|
||||
case _Flags::_Sign::__plus:
|
||||
*__buf++ = '+';
|
||||
break;
|
||||
case _Flags::_Sign::__space:
|
||||
*__buf++ = ' ';
|
||||
break;
|
||||
}
|
||||
|
||||
return __buf;
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr char __hex_to_upper(char c) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
return 'A';
|
||||
case 'b':
|
||||
return 'B';
|
||||
case 'c':
|
||||
return 'C';
|
||||
case 'd':
|
||||
return 'D';
|
||||
case 'e':
|
||||
return 'E';
|
||||
case 'f':
|
||||
return 'F';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the required grouping based on the size of the input.
|
||||
*
|
||||
* The grouping's last element will be repeated. For simplicity this repeating
|
||||
* is unwrapped based on the length of the input. (When the input is short some
|
||||
* groups are not processed.)
|
||||
*
|
||||
* @returns The size of the groups to write. This means the number of
|
||||
* separator characters written is size() - 1.
|
||||
*
|
||||
* @note Since zero-sized groups cause issues they are silently ignored.
|
||||
*
|
||||
* @note The grouping field of the locale is always a @c std::string,
|
||||
* regardless whether the @c std::numpunct's type is @c char or @c wchar_t.
|
||||
*/
|
||||
_LIBCPP_HIDE_FROM_ABI inline string
|
||||
__determine_grouping(ptrdiff_t __size, const string& __grouping) {
|
||||
_LIBCPP_ASSERT(!__grouping.empty() && __size > __grouping[0],
|
||||
"The slow grouping formatting is used while there will be no "
|
||||
"separators written");
|
||||
string __r;
|
||||
auto __end = __grouping.end() - 1;
|
||||
auto __ptr = __grouping.begin();
|
||||
|
||||
while (true) {
|
||||
__size -= *__ptr;
|
||||
if (__size > 0)
|
||||
__r.push_back(*__ptr);
|
||||
else {
|
||||
// __size <= 0 so the value pushed will be <= *__ptr.
|
||||
__r.push_back(*__ptr + __size);
|
||||
return __r;
|
||||
}
|
||||
|
||||
// Proceed to the next group.
|
||||
if (__ptr != __end) {
|
||||
do {
|
||||
++__ptr;
|
||||
// Skip grouping with a width of 0.
|
||||
} while (*__ptr == 0 && __ptr != __end);
|
||||
}
|
||||
}
|
||||
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
|
||||
template <class _Parser>
|
||||
requires __formatter::__char_type<typename _Parser::char_type>
|
||||
class _LIBCPP_TEMPLATE_VIS __formatter_integral : public _Parser {
|
||||
public:
|
||||
using _CharT = typename _Parser::char_type;
|
||||
|
||||
template <integral _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));
|
||||
|
||||
if (this->__type == _Flags::_Type::__char)
|
||||
return __format_as_char(__value, __ctx);
|
||||
|
||||
if constexpr (unsigned_integral<_Tp>)
|
||||
return __format_unsigned_integral(__value, false, __ctx);
|
||||
else {
|
||||
// Depending on the std-format-spec string the sign and the value
|
||||
// might not be outputted together:
|
||||
// - alternate form may insert a prefix string.
|
||||
// - zero-padding may insert additional '0' characters.
|
||||
// Therefore the value is processed as a positive unsigned value.
|
||||
// The function @ref __insert_sign will a '-' when the value was negative.
|
||||
auto __r = __to_unsigned_like(__value);
|
||||
bool __negative = __value < 0;
|
||||
if (__negative)
|
||||
__r = __complement(__r);
|
||||
|
||||
return __format_unsigned_integral(__r, __negative, __ctx);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** Generic formatting for format-type c. */
|
||||
_LIBCPP_HIDE_FROM_ABI auto __format_as_char(integral auto __value,
|
||||
auto& __ctx)
|
||||
-> decltype(__ctx.out()) {
|
||||
if (this->__alignment == _Flags::_Alignment::__default)
|
||||
this->__alignment = _Flags::_Alignment::__right;
|
||||
|
||||
using _Tp = decltype(__value);
|
||||
if constexpr (!same_as<_CharT, _Tp>) {
|
||||
// cmp_less and cmp_greater can't be used for character types.
|
||||
if constexpr (signed_integral<_CharT> == signed_integral<_Tp>) {
|
||||
if (__value < numeric_limits<_CharT>::min() ||
|
||||
__value > numeric_limits<_CharT>::max())
|
||||
__throw_format_error(
|
||||
"Integral value outside the range of the char type");
|
||||
} else if constexpr (signed_integral<_CharT>) {
|
||||
// _CharT is signed _Tp is unsigned
|
||||
if (__value >
|
||||
static_cast<make_unsigned_t<_CharT>>(numeric_limits<_CharT>::max()))
|
||||
__throw_format_error(
|
||||
"Integral value outside the range of the char type");
|
||||
} else {
|
||||
// _CharT is unsigned _Tp is signed
|
||||
if (__value < 0 || static_cast<make_unsigned_t<_Tp>>(__value) >
|
||||
numeric_limits<_CharT>::max())
|
||||
__throw_format_error(
|
||||
"Integral value outside the range of the char type");
|
||||
}
|
||||
}
|
||||
|
||||
const auto __c = static_cast<_CharT>(__value);
|
||||
return __write(_VSTD::addressof(__c), _VSTD::addressof(__c) + 1,
|
||||
__ctx.out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic formatting for format-type bBdoxX.
|
||||
*
|
||||
* This small wrapper allocates a buffer with the required size. Then calls
|
||||
* the real formatter with the buffer and the prefix for the base.
|
||||
*/
|
||||
_LIBCPP_HIDE_FROM_ABI auto
|
||||
__format_unsigned_integral(unsigned_integral auto __value, bool __negative,
|
||||
auto& __ctx) -> decltype(__ctx.out()) {
|
||||
switch (this->__type) {
|
||||
case _Flags::_Type::__binary_lower_case: {
|
||||
array<char, __buffer_size<decltype(__value), 2>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 2, __ctx, "0b");
|
||||
}
|
||||
case _Flags::_Type::__binary_upper_case: {
|
||||
array<char, __buffer_size<decltype(__value), 2>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 2, __ctx, "0B");
|
||||
}
|
||||
case _Flags::_Type::__octal: {
|
||||
// Octal is special; if __value == 0 there's no prefix.
|
||||
array<char, __buffer_size<decltype(__value), 8>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 8, __ctx,
|
||||
__value != 0 ? "0" : nullptr);
|
||||
}
|
||||
case _Flags::_Type::__decimal: {
|
||||
array<char, __buffer_size<decltype(__value), 10>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 10, __ctx, nullptr);
|
||||
}
|
||||
case _Flags::_Type::__hexadecimal_lower_case: {
|
||||
array<char, __buffer_size<decltype(__value), 16>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 16, __ctx, "0x");
|
||||
}
|
||||
case _Flags::_Type::__hexadecimal_upper_case: {
|
||||
array<char, __buffer_size<decltype(__value), 16>()> __array;
|
||||
return __format_unsigned_integral(__array.begin(), __array.end(), __value,
|
||||
__negative, 16, __ctx, "0X");
|
||||
}
|
||||
default:
|
||||
_LIBCPP_ASSERT(false, "The parser should have validated the type");
|
||||
_LIBCPP_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
requires(same_as<char, _Tp> || same_as<wchar_t, _Tp>) _LIBCPP_HIDE_FROM_ABI
|
||||
auto __write(const _Tp* __first, const _Tp* __last, auto __out_it)
|
||||
-> decltype(__out_it) {
|
||||
|
||||
unsigned __size = __last - __first;
|
||||
if (this->__type != _Flags::_Type::__hexadecimal_upper_case) [[likely]] {
|
||||
if (__size >= this->__width)
|
||||
return _VSTD::copy(__first, __last, _VSTD::move(__out_it));
|
||||
|
||||
return __formatter::__write(_VSTD::move(__out_it), __first, __last,
|
||||
__size, this->__width, this->__fill,
|
||||
this->__alignment);
|
||||
}
|
||||
|
||||
// this->__type == _Flags::_Type::__hexadecimal_upper_case
|
||||
// This means all characters in the range [a-f] need to be changed to their
|
||||
// uppercase representation. The transformation is done as transformation
|
||||
// in the output routine instead of before. This avoids another pass over
|
||||
// the data.
|
||||
// TODO FMT See whether it's possible to do this transformation during the
|
||||
// conversion. (This probably requires changing std::to_chars' alphabet.)
|
||||
if (__size >= this->__width)
|
||||
return _VSTD::transform(__first, __last, _VSTD::move(__out_it),
|
||||
__hex_to_upper);
|
||||
|
||||
return __formatter::__write(_VSTD::move(__out_it), __first, __last, __size,
|
||||
__hex_to_upper, this->__width, this->__fill,
|
||||
this->__alignment);
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI auto
|
||||
__format_unsigned_integral(char* __begin, char* __end,
|
||||
unsigned_integral auto __value, bool __negative,
|
||||
int __base, auto& __ctx, const char* __prefix)
|
||||
-> decltype(__ctx.out()) {
|
||||
char* __first = __insert_sign(__begin, __negative, this->__sign);
|
||||
if (this->__alternate_form && __prefix)
|
||||
while (*__prefix)
|
||||
*__first++ = *__prefix++;
|
||||
|
||||
char* __last = __to_buffer(__first, __end, __value, __base);
|
||||
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
||||
if (this->__locale_specific_form) {
|
||||
const auto& __np = use_facet<numpunct<_CharT>>(__ctx.locale());
|
||||
string __grouping = __np.grouping();
|
||||
ptrdiff_t __size = __last - __first;
|
||||
// Writing the grouped form has more overhead than the normal output
|
||||
// routines. If there will be no separators written the locale-specific
|
||||
// form is identical to the normal routine. Test whether to grouped form
|
||||
// is required.
|
||||
if (!__grouping.empty() && __size > __grouping[0])
|
||||
return __format_grouping(__ctx.out(), __begin, __first, __last,
|
||||
__determine_grouping(__size, __grouping),
|
||||
__np.thousands_sep());
|
||||
}
|
||||
#endif
|
||||
auto __out_it = __ctx.out();
|
||||
if (this->__alignment != _Flags::_Alignment::__default)
|
||||
__first = __begin;
|
||||
else {
|
||||
// __buf contains [sign][prefix]data
|
||||
// ^ location of __first
|
||||
// The zero padding is done like:
|
||||
// - Write [sign][prefix]
|
||||
// - Write data right aligned with '0' as fill character.
|
||||
__out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
|
||||
this->__alignment = _Flags::_Alignment::__right;
|
||||
this->__fill = _CharT('0');
|
||||
unsigned __size = __first - __begin;
|
||||
this->__width -= _VSTD::min(__size, this->__width);
|
||||
}
|
||||
|
||||
return __write(__first, __last, _VSTD::move(__out_it));
|
||||
}
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
||||
/** Format's the locale-specific form's groupings. */
|
||||
template <class _OutIt, class _CharT>
|
||||
_LIBCPP_HIDE_FROM_ABI _OutIt
|
||||
__format_grouping(_OutIt __out_it, const char* __begin, const char* __first,
|
||||
const char* __last, string&& __grouping, _CharT __sep) {
|
||||
|
||||
// TODO FMT This function duplicates some functionality of the normal
|
||||
// output routines. Evaluate whether these parts can be efficiently
|
||||
// combined with the existing routines.
|
||||
|
||||
unsigned __size = (__first - __begin) + // [sign][prefix]
|
||||
(__last - __first) + // data
|
||||
(__grouping.size() - 1); // number of separator characters
|
||||
|
||||
__formatter::__padding_size_result __padding = {0, 0};
|
||||
if (this->__alignment == _Flags::_Alignment::__default) {
|
||||
// Write [sign][prefix].
|
||||
__out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
|
||||
|
||||
if (this->__width > __size) {
|
||||
// Write zero padding.
|
||||
__padding.__before = this->__width - __size;
|
||||
__out_it = _VSTD::fill_n(_VSTD::move(__out_it), this->__width - __size,
|
||||
_CharT('0'));
|
||||
}
|
||||
} else {
|
||||
if (this->__width > __size) {
|
||||
// Determine padding and write padding.
|
||||
__padding = __formatter::__padding_size(__size, this->__width,
|
||||
this->__alignment);
|
||||
|
||||
__out_it = _VSTD::fill_n(_VSTD::move(__out_it), __padding.__before,
|
||||
this->__fill);
|
||||
}
|
||||
// Write [sign][prefix].
|
||||
__out_it = _VSTD::copy(__begin, __first, _VSTD::move(__out_it));
|
||||
}
|
||||
|
||||
auto __r = __grouping.rbegin();
|
||||
auto __e = __grouping.rend() - 1;
|
||||
_LIBCPP_ASSERT(__r != __e, "The slow grouping formatting is used while "
|
||||
"there will be no separators written.");
|
||||
// The output is divided in small groups of numbers to write:
|
||||
// - A group before the first separator.
|
||||
// - A separator and a group, repeated for the number of separators.
|
||||
// - A group after the last separator.
|
||||
// This loop achieves that process by testing the termination condition
|
||||
// midway in the loop.
|
||||
//
|
||||
// TODO FMT This loop evaluates the loop invariant `this->__type !=
|
||||
// _Flags::_Type::__hexadecimal_upper_case` for every iteration. (This test
|
||||
// happens in the __write call.) Benchmark whether making two loops and
|
||||
// hoisting the invariant is worth the effort.
|
||||
while (true) {
|
||||
if (this->__type == _Flags::_Type::__hexadecimal_upper_case) {
|
||||
__last = __first + *__r;
|
||||
__out_it = _VSTD::transform(__first, __last, _VSTD::move(__out_it),
|
||||
__hex_to_upper);
|
||||
__first = __last;
|
||||
} else {
|
||||
__out_it = _VSTD::copy_n(__first, *__r, _VSTD::move(__out_it));
|
||||
__first += *__r;
|
||||
}
|
||||
|
||||
if (__r == __e)
|
||||
break;
|
||||
|
||||
++__r;
|
||||
*__out_it++ = __sep;
|
||||
}
|
||||
|
||||
return _VSTD::fill_n(_VSTD::move(__out_it), __padding.__after,
|
||||
this->__fill);
|
||||
}
|
||||
#endif // _LIBCPP_HAS_NO_LOCALIZATION
|
||||
};
|
||||
|
||||
} // namespace __format_spec
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
#endif //_LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___FORMAT_FORMATTER_INTEGRAL_H
|
||||
@@ -606,9 +606,7 @@ class _LIBCPP_TEMPLATE_VIS __parser_integral
|
||||
public:
|
||||
using char_type = _CharT;
|
||||
|
||||
// TODO FMT This class probably doesn't need public member functions after
|
||||
// format.string.std/std_format_spec_integral.pass.cpp has been retired.
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The low-level std-format-spec parse function.
|
||||
*
|
||||
@@ -619,7 +617,7 @@ public:
|
||||
*
|
||||
* @returns The iterator pointing at the last parsed character.
|
||||
*/
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr auto parse(auto& __parse_ctx)
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr auto __parse(auto& __parse_ctx)
|
||||
-> decltype(__parse_ctx.begin()) {
|
||||
auto __begin = __parse_ctx.begin();
|
||||
auto __end = __parse_ctx.end();
|
||||
@@ -661,7 +659,6 @@ public:
|
||||
return __begin;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handles the post-parsing updates for the integer types.
|
||||
*
|
||||
|
||||
@@ -275,9 +275,11 @@ namespace std {
|
||||
#include <__format/format_args.h>
|
||||
#include <__format/format_context.h>
|
||||
#include <__format/format_error.h>
|
||||
#include <__format/format_fwd.h>
|
||||
#include <__format/format_parse_context.h>
|
||||
#include <__format/format_string.h>
|
||||
#include <__format/formatter.h>
|
||||
#include <__format/formatter_integer.h>
|
||||
#include <__format/formatter_string.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
#include <__variant/monostate.h>
|
||||
@@ -385,9 +387,6 @@ private:
|
||||
template <class _Uv>
|
||||
_LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx)
|
||||
-> decltype(__ctx.out())
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
requires(!same_as<_Uv, __int128_t> && !same_as<_Uv, __uint128_t>)
|
||||
#endif
|
||||
{
|
||||
// TODO FMT Implement using formatting arguments
|
||||
// TODO FMT Improve PoC since using std::to_string is inefficient.
|
||||
@@ -400,20 +399,6 @@ private:
|
||||
*__out_it++ = __str[__i];
|
||||
return __out_it;
|
||||
}
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
template <class _Uv>
|
||||
_LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx)
|
||||
-> decltype(__ctx.out()) requires(same_as<_Uv, __int128_t> ||
|
||||
same_as<_Uv, __uint128_t>) {
|
||||
using _To = conditional_t<is_signed_v<_Uv>, long long, unsigned long long>;
|
||||
// TODO FMT Implement full 128-bit support.
|
||||
if (__value < numeric_limits<_To>::min() ||
|
||||
__value > numeric_limits<_To>::max())
|
||||
__throw_format_error("128-bit value is outside of implemented range");
|
||||
|
||||
return __handle_format(static_cast<_To>(__value), __ctx);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
} // namespace __format
|
||||
|
||||
@@ -456,50 +441,6 @@ struct _LIBCPP_TEMPLATE_VIS formatter<bool, _CharT> {
|
||||
}
|
||||
};
|
||||
|
||||
// Signed integral types.
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<signed char, _CharT>
|
||||
: public __format::__formatter_arithmetic<signed char, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<short, _CharT>
|
||||
: public __format::__formatter_arithmetic<short, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<int, _CharT>
|
||||
: public __format::__formatter_arithmetic<int, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<long, _CharT>
|
||||
: public __format::__formatter_arithmetic<long, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<long long, _CharT>
|
||||
: public __format::__formatter_arithmetic<long long, _CharT> {};
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<__int128_t, _CharT>
|
||||
: public __format::__formatter_arithmetic<__int128_t, _CharT> {};
|
||||
#endif
|
||||
|
||||
// Unsigned integral types.
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned char, _CharT>
|
||||
: public __format::__formatter_arithmetic<unsigned char, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned short, _CharT>
|
||||
: public __format::__formatter_arithmetic<unsigned short, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned, _CharT>
|
||||
: public __format::__formatter_arithmetic<unsigned, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned long, _CharT>
|
||||
: public __format::__formatter_arithmetic<unsigned long, _CharT> {};
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned long long, _CharT>
|
||||
: public __format::__formatter_arithmetic<unsigned long long, _CharT> {};
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
template <class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<__uint128_t, _CharT>
|
||||
: public __format::__formatter_arithmetic<__uint128_t, _CharT> {};
|
||||
#endif
|
||||
|
||||
// Floating point types.
|
||||
// TODO FMT There are no replacements for the floating point stubs due to not
|
||||
// having floating point support in std::to_chars yet. These stubs aren't
|
||||
|
||||
@@ -450,6 +450,8 @@ module std [system] {
|
||||
module format_parse_context { private header "__format/format_parse_context.h" }
|
||||
module format_string { private header "__format/format_string.h" }
|
||||
module formatter { private header "__format/formatter.h" }
|
||||
module formatter_integer { private header "__format/formatter_integer.h" }
|
||||
module formatter_integral { private header "__format/formatter_integral.h" }
|
||||
module formatter_string { private header "__format/formatter_string.h" }
|
||||
module parser_std_format_spec { private header "__format/parser_std_format_spec.h" }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__format/formatter_integer.h'}}
|
||||
#include <__format/formatter_integer.h>
|
||||
@@ -0,0 +1,16 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: modules-build
|
||||
|
||||
// WARNING: This test was generated by 'generate_private_header_tests.py'
|
||||
// and should not be edited manually.
|
||||
|
||||
// expected-error@*:* {{use of private header from outside its module: '__format/formatter_integral.h'}}
|
||||
#include <__format/formatter_integral.h>
|
||||
@@ -14,10 +14,6 @@
|
||||
// Tests the parsing of the format string as specified in [format.string.std].
|
||||
// It validates whether the std-format-spec is valid for a string type.
|
||||
|
||||
// TODO FMT This test should removed once the integer parser is implemented.
|
||||
// The integral specific fields are all tested for the integer, making this
|
||||
// test redundant.
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
||||
@@ -34,19 +30,19 @@
|
||||
using namespace std::__format_spec;
|
||||
|
||||
template <class CharT>
|
||||
using Parser = __parser_integral<CharT>;
|
||||
using Parser = __parser_integer<CharT>;
|
||||
|
||||
template <class CharT>
|
||||
struct Expected {
|
||||
CharT fill = CharT(' ');
|
||||
_Flags::_Alignment alignment = _Flags::_Alignment::__default;
|
||||
_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;
|
||||
bool locale_specific_form = false;
|
||||
_Flags::_Type type = _Flags::_Type::__default;
|
||||
_Flags::_Type type = _Flags::_Type::__decimal;
|
||||
};
|
||||
|
||||
template <class CharT>
|
||||
@@ -107,6 +103,9 @@ constexpr void test() {
|
||||
assert(parser.__type == _Flags::_Type::__default);
|
||||
|
||||
test({}, 0, CSTR("}"));
|
||||
test({}, 1, CSTR("d}"));
|
||||
test({.alignment = _Flags::_Alignment::__left, .type = _Flags::_Type::__char},
|
||||
1, CSTR("c}"));
|
||||
|
||||
// *** Align-fill ***
|
||||
test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}"));
|
||||
@@ -130,8 +129,22 @@ constexpr void test() {
|
||||
test({.sign = _Flags::_Sign::__plus}, 1, CSTR("+}"));
|
||||
test({.sign = _Flags::_Sign::__space}, 1, CSTR(" }"));
|
||||
|
||||
test({.sign = _Flags::_Sign::__minus}, 2, CSTR("-d}"));
|
||||
test({.sign = _Flags::_Sign::__plus}, 2, CSTR("+d}"));
|
||||
test({.sign = _Flags::_Sign::__space}, 2, CSTR(" d}"));
|
||||
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR("-c"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR("+c"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR(" c"));
|
||||
|
||||
// *** Alternate form ***
|
||||
test({.alternate_form = true}, 1, CSTR("#}"));
|
||||
test({.alternate_form = true}, 2, CSTR("#d}"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"An alternate form field isn't allowed in this format-spec", CSTR("#c"));
|
||||
|
||||
// *** Zero padding ***
|
||||
// TODO FMT What to do with zero-padding without a width?
|
||||
@@ -141,9 +154,32 @@ constexpr void test() {
|
||||
// width, except when applied to an infinity or NaN.
|
||||
// Obviously it makes no sense, but should it be allowed or is it a format
|
||||
// errror?
|
||||
test({.zero_padding = true}, 1, CSTR("0}"));
|
||||
test({.alignment = _Flags::_Alignment::__center, .zero_padding = true}, 2,
|
||||
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}"));
|
||||
|
||||
test({.alignment = _Flags::_Alignment::__default, .zero_padding = true}, 2,
|
||||
CSTR("0d}"));
|
||||
test({.alignment = _Flags::_Alignment::__left, .zero_padding = false}, 3,
|
||||
CSTR("<0d}"));
|
||||
test({.alignment = _Flags::_Alignment::__center, .zero_padding = false}, 3,
|
||||
CSTR("^0d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right, .zero_padding = false}, 3,
|
||||
CSTR(">0d}"));
|
||||
|
||||
test_exception<Parser<CharT>>(
|
||||
"A zero-padding field isn't allowed in this format-spec", CSTR("0c"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A zero-padding field isn't allowed in this format-spec", CSTR("<0c"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A zero-padding field isn't allowed in this format-spec", CSTR("^0c"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A zero-padding field isn't allowed in this format-spec", CSTR(">0c"));
|
||||
|
||||
// *** Width ***
|
||||
test({.width = 0, .width_as_arg = false}, 0, CSTR("}"));
|
||||
@@ -210,25 +246,27 @@ constexpr void test() {
|
||||
|
||||
// *** Locale-specific form ***
|
||||
test({.locale_specific_form = true}, 1, CSTR("L}"));
|
||||
test({.locale_specific_form = true, .type = _Flags::_Type::__decimal}, 2,
|
||||
CSTR("Ld}"));
|
||||
test({.locale_specific_form = true, .type = _Flags::_Type::__char}, 2,
|
||||
CSTR("Lc}"));
|
||||
test({.locale_specific_form = true}, 2, CSTR("Ld}"));
|
||||
// Note the flag is allowed, but has no effect.
|
||||
test({.alignment = _Flags::_Alignment::__left,
|
||||
.locale_specific_form = true,
|
||||
.type = _Flags::_Type::__char},
|
||||
2, CSTR("Lc}"));
|
||||
|
||||
// *** Type ***
|
||||
|
||||
{
|
||||
const char* unsuported_type =
|
||||
"The format-spec type has a type not supported for an integer 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("A}"));
|
||||
test({.type = _Flags::_Type::__binary_upper_case}, 1, CSTR("B}"));
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("C}"));
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("D}"));
|
||||
test({.type = _Flags::_Type::__scientific_upper_case}, 1, CSTR("E}"));
|
||||
test({.type = _Flags::_Type::__fixed_upper_case}, 1, CSTR("F}"));
|
||||
test({.type = _Flags::_Type::__general_upper_case}, 1, CSTR("G}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("E}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("F}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, 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}"));
|
||||
@@ -249,14 +287,15 @@ constexpr void test() {
|
||||
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("a}"));
|
||||
test({.type = _Flags::_Type::__binary_lower_case}, 1, CSTR("b}"));
|
||||
test({.type = _Flags::_Type::__char}, 1, CSTR("c}"));
|
||||
test({.alignment = _Flags::_Alignment::__left,
|
||||
.type = _Flags::_Type::__char},
|
||||
1, CSTR("c}"));
|
||||
test({.type = _Flags::_Type::__decimal}, 1, CSTR("d}"));
|
||||
test({.type = _Flags::_Type::__scientific_lower_case}, 1, CSTR("e}"));
|
||||
test({.type = _Flags::_Type::__fixed_lower_case}, 1, CSTR("f}"));
|
||||
test({.type = _Flags::_Type::__general_lower_case}, 1, CSTR("g}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("e}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("f}"));
|
||||
test_exception<Parser<CharT>>(unsuported_type, 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}"));
|
||||
@@ -265,10 +304,10 @@ constexpr void test() {
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("m}"));
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("n}"));
|
||||
test({.type = _Flags::_Type::__octal}, 1, CSTR("o}"));
|
||||
test({.type = _Flags::_Type::__pointer}, 1, CSTR("p}"));
|
||||
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({.type = _Flags::_Type::__string}, 1, CSTR("s}"));
|
||||
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}"));
|
||||
@@ -285,13 +324,6 @@ constexpr void test() {
|
||||
constexpr bool test() {
|
||||
test<char>();
|
||||
test<wchar_t>();
|
||||
#ifndef _LIBCPP_HAS_NO_CHAR8_T
|
||||
test<char8_t>();
|
||||
#endif
|
||||
#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS
|
||||
test<char16_t>();
|
||||
test<char32_t>();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -260,6 +260,333 @@ void format_string_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
format_test_string_unicode<CharT>(check);
|
||||
}
|
||||
|
||||
template <class I, class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_integer_as_integer(TestFunction check,
|
||||
ExceptionTest check_exception) {
|
||||
// *** align-fill & width ***
|
||||
check(STR("answer is '42'"), STR("answer is '{:<1}'"), I(42));
|
||||
check(STR("answer is '42'"), STR("answer is '{:<2}'"), I(42));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<3}'"), I(42));
|
||||
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:7}'"), I(42));
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:>7}'"), I(42));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<7}'"), I(42));
|
||||
check(STR("answer is ' 42 '"), STR("answer is '{:^7}'"), I(42));
|
||||
|
||||
check(STR("answer is '*****42'"), STR("answer is '{:*>7}'"), I(42));
|
||||
check(STR("answer is '42*****'"), STR("answer is '{:*<7}'"), I(42));
|
||||
check(STR("answer is '**42***'"), STR("answer is '{:*^7}'"), I(42));
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:>07}'"), I(42));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<07}'"), I(42));
|
||||
check(STR("answer is ' 42 '"), STR("answer is '{:^07}'"), I(42));
|
||||
|
||||
// *** Sign ***
|
||||
if constexpr (std::signed_integral<I>)
|
||||
check(STR("answer is -42"), STR("answer is {}"), I(-42));
|
||||
check(STR("answer is 0"), STR("answer is {}"), I(0));
|
||||
check(STR("answer is 42"), STR("answer is {}"), I(42));
|
||||
|
||||
if constexpr (std::signed_integral<I>)
|
||||
check(STR("answer is -42"), STR("answer is {:-}"), I(-42));
|
||||
check(STR("answer is 0"), STR("answer is {:-}"), I(0));
|
||||
check(STR("answer is 42"), STR("answer is {:-}"), I(42));
|
||||
|
||||
if constexpr (std::signed_integral<I>)
|
||||
check(STR("answer is -42"), STR("answer is {:+}"), I(-42));
|
||||
check(STR("answer is +0"), STR("answer is {:+}"), I(0));
|
||||
check(STR("answer is +42"), STR("answer is {:+}"), I(42));
|
||||
|
||||
if constexpr (std::signed_integral<I>)
|
||||
check(STR("answer is -42"), STR("answer is {: }"), I(-42));
|
||||
check(STR("answer is 0"), STR("answer is {: }"), I(0));
|
||||
check(STR("answer is 42"), STR("answer is {: }"), I(42));
|
||||
|
||||
// *** alternate form ***
|
||||
if constexpr (std::signed_integral<I>) {
|
||||
check(STR("answer is -42"), STR("answer is {:#}"), I(-42));
|
||||
check(STR("answer is -42"), STR("answer is {:#d}"), I(-42));
|
||||
check(STR("answer is -101010"), STR("answer is {:b}"), I(-42));
|
||||
check(STR("answer is -0b101010"), STR("answer is {:#b}"), I(-42));
|
||||
check(STR("answer is -0B101010"), STR("answer is {:#B}"), I(-42));
|
||||
check(STR("answer is -52"), STR("answer is {:o}"), I(-42));
|
||||
check(STR("answer is -052"), STR("answer is {:#o}"), I(-42));
|
||||
check(STR("answer is -2a"), STR("answer is {:x}"), I(-42));
|
||||
check(STR("answer is -0x2a"), STR("answer is {:#x}"), I(-42));
|
||||
check(STR("answer is -2A"), STR("answer is {:X}"), I(-42));
|
||||
check(STR("answer is -0X2A"), STR("answer is {:#X}"), I(-42));
|
||||
}
|
||||
check(STR("answer is 0"), STR("answer is {:#}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:#d}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:b}"), I(0));
|
||||
check(STR("answer is 0b0"), STR("answer is {:#b}"), I(0));
|
||||
check(STR("answer is 0B0"), STR("answer is {:#B}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:o}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:#o}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:x}"), I(0));
|
||||
check(STR("answer is 0x0"), STR("answer is {:#x}"), I(0));
|
||||
check(STR("answer is 0"), STR("answer is {:X}"), I(0));
|
||||
check(STR("answer is 0X0"), STR("answer is {:#X}"), I(0));
|
||||
|
||||
check(STR("answer is +42"), STR("answer is {:+#}"), I(42));
|
||||
check(STR("answer is +42"), STR("answer is {:+#d}"), I(42));
|
||||
check(STR("answer is +101010"), STR("answer is {:+b}"), I(42));
|
||||
check(STR("answer is +0b101010"), STR("answer is {:+#b}"), I(42));
|
||||
check(STR("answer is +0B101010"), STR("answer is {:+#B}"), I(42));
|
||||
check(STR("answer is +52"), STR("answer is {:+o}"), I(42));
|
||||
check(STR("answer is +052"), STR("answer is {:+#o}"), I(42));
|
||||
check(STR("answer is +2a"), STR("answer is {:+x}"), I(42));
|
||||
check(STR("answer is +0x2a"), STR("answer is {:+#x}"), I(42));
|
||||
check(STR("answer is +2A"), STR("answer is {:+X}"), I(42));
|
||||
check(STR("answer is +0X2A"), STR("answer is {:+#X}"), I(42));
|
||||
|
||||
// *** zero-padding & width ***
|
||||
if constexpr (std::signed_integral<I>) {
|
||||
check(STR("answer is -00000000042"), STR("answer is {:#012}"), I(-42));
|
||||
check(STR("answer is -00000000042"), STR("answer is {:#012d}"), I(-42));
|
||||
check(STR("answer is -00000101010"), STR("answer is {:012b}"), I(-42));
|
||||
check(STR("answer is -0b000101010"), STR("answer is {:#012b}"), I(-42));
|
||||
check(STR("answer is -0B000101010"), STR("answer is {:#012B}"), I(-42));
|
||||
check(STR("answer is -00000000052"), STR("answer is {:012o}"), I(-42));
|
||||
check(STR("answer is -00000000052"), STR("answer is {:#012o}"), I(-42));
|
||||
check(STR("answer is -0000000002a"), STR("answer is {:012x}"), I(-42));
|
||||
check(STR("answer is -0x00000002a"), STR("answer is {:#012x}"), I(-42));
|
||||
check(STR("answer is -0000000002A"), STR("answer is {:012X}"), I(-42));
|
||||
check(STR("answer is -0X00000002A"), STR("answer is {:#012X}"), I(-42));
|
||||
}
|
||||
|
||||
check(STR("answer is 000000000000"), STR("answer is {:#012}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:#012d}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:012b}"), I(0));
|
||||
check(STR("answer is 0b0000000000"), STR("answer is {:#012b}"), I(0));
|
||||
check(STR("answer is 0B0000000000"), STR("answer is {:#012B}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:012o}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:#012o}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:012x}"), I(0));
|
||||
check(STR("answer is 0x0000000000"), STR("answer is {:#012x}"), I(0));
|
||||
check(STR("answer is 000000000000"), STR("answer is {:012X}"), I(0));
|
||||
check(STR("answer is 0X0000000000"), STR("answer is {:#012X}"), I(0));
|
||||
|
||||
check(STR("answer is +00000000042"), STR("answer is {:+#012}"), I(42));
|
||||
check(STR("answer is +00000000042"), STR("answer is {:+#012d}"), I(42));
|
||||
check(STR("answer is +00000101010"), STR("answer is {:+012b}"), I(42));
|
||||
check(STR("answer is +0b000101010"), STR("answer is {:+#012b}"), I(42));
|
||||
check(STR("answer is +0B000101010"), STR("answer is {:+#012B}"), I(42));
|
||||
check(STR("answer is +00000000052"), STR("answer is {:+012o}"), I(42));
|
||||
check(STR("answer is +00000000052"), STR("answer is {:+#012o}"), I(42));
|
||||
check(STR("answer is +0000000002a"), STR("answer is {:+012x}"), I(42));
|
||||
check(STR("answer is +0x00000002a"), STR("answer is {:+#012x}"), I(42));
|
||||
check(STR("answer is +0000000002A"), STR("answer is {:+012X}"), I(42));
|
||||
check(STR("answer is +0X00000002A"), STR("answer is {:+#012X}"), I(42));
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.}"), I(0));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.0}"), I(0));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.42}"), I(0));
|
||||
|
||||
// *** locale-specific form ***
|
||||
// See locale-specific_form.pass.cpp
|
||||
|
||||
// *** type ***
|
||||
for (const auto& fmt : invalid_types<CharT>("bBcdoxX"))
|
||||
check_exception(
|
||||
"The format-spec type has a type not supported for an integer argument",
|
||||
fmt, 42);
|
||||
}
|
||||
|
||||
template <class I, class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_integer_as_char(TestFunction check,
|
||||
ExceptionTest check_exception) {
|
||||
// *** align-fill & width ***
|
||||
check(STR("answer is '* '"), STR("answer is '{:6c}'"), I(42));
|
||||
check(STR("answer is ' *'"), STR("answer is '{:>6c}'"), I(42));
|
||||
check(STR("answer is '* '"), STR("answer is '{:<6c}'"), I(42));
|
||||
check(STR("answer is ' * '"), STR("answer is '{:^6c}'"), I(42));
|
||||
|
||||
check(STR("answer is '-----*'"), STR("answer is '{:->6c}'"), I(42));
|
||||
check(STR("answer is '*-----'"), STR("answer is '{:-<6c}'"), I(42));
|
||||
check(STR("answer is '--*---'"), STR("answer is '{:-^6c}'"), I(42));
|
||||
|
||||
// *** Sign ***
|
||||
check(STR("answer is *"), STR("answer is {:c}"), I(42));
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("answer is {:-c}"), I(42));
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("answer is {:+c}"), I(42));
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("answer is {: c}"), I(42));
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("An alternate form field isn't allowed in this format-spec",
|
||||
STR("answer is {:#c}"), I(42));
|
||||
|
||||
// *** zero-padding & width ***
|
||||
check_exception("A zero-padding field isn't allowed in this format-spec",
|
||||
STR("answer is {:01c}"), I(42));
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.c}"), I(0));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.0c}"), I(0));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.42c}"), I(0));
|
||||
|
||||
// *** locale-specific form ***
|
||||
// Note it has no effect but it's allowed.
|
||||
check(STR("answer is '*'"), STR("answer is '{:Lc}'"), I(42));
|
||||
|
||||
// *** type ***
|
||||
for (const auto& fmt : invalid_types<CharT>("bBcdoxX"))
|
||||
check_exception(
|
||||
"The format-spec type has a type not supported for an integer argument",
|
||||
fmt, I(42));
|
||||
|
||||
// *** Validate range ***
|
||||
// TODO FMT Update test after adding 128-bit support.
|
||||
if constexpr (sizeof(I) <= sizeof(long long)) {
|
||||
// The code has some duplications to keep the if statement readable.
|
||||
if constexpr (std::signed_integral<CharT>) {
|
||||
if constexpr (std::signed_integral<I> && sizeof(I) > sizeof(CharT)) {
|
||||
check_exception("Integral value outside the range of the char type",
|
||||
STR("{:c}"), std::numeric_limits<I>::min());
|
||||
check_exception("Integral value outside the range of the char type",
|
||||
STR("{:c}"), std::numeric_limits<I>::max());
|
||||
} else if constexpr (std::unsigned_integral<I> &&
|
||||
sizeof(I) >= sizeof(CharT)) {
|
||||
check_exception("Integral value outside the range of the char type",
|
||||
STR("{:c}"), std::numeric_limits<I>::max());
|
||||
}
|
||||
} else if constexpr (sizeof(I) > sizeof(CharT)) {
|
||||
check_exception("Integral value outside the range of the char type",
|
||||
STR("{:c}"), std::numeric_limits<I>::max());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class I, class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_integer(TestFunction check, ExceptionTest check_exception) {
|
||||
format_test_integer_as_integer<I, CharT>(check, check_exception);
|
||||
format_test_integer_as_char<I, CharT>(check, check_exception);
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_signed_integer(TestFunction check,
|
||||
ExceptionTest check_exception) {
|
||||
format_test_integer<signed char, CharT>(check, check_exception);
|
||||
format_test_integer<short, CharT>(check, check_exception);
|
||||
format_test_integer<int, CharT>(check, check_exception);
|
||||
format_test_integer<long, CharT>(check, check_exception);
|
||||
format_test_integer<long long, CharT>(check, check_exception);
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
format_test_integer<__int128_t, CharT>(check, check_exception);
|
||||
#endif
|
||||
// *** check the minma and maxima ***
|
||||
check(STR("-0b10000000"), STR("{:#b}"), std::numeric_limits<int8_t>::min());
|
||||
check(STR("-0200"), STR("{:#o}"), std::numeric_limits<int8_t>::min());
|
||||
check(STR("-128"), STR("{:#}"), std::numeric_limits<int8_t>::min());
|
||||
check(STR("-0x80"), STR("{:#x}"), std::numeric_limits<int8_t>::min());
|
||||
|
||||
check(STR("-0b1000000000000000"), STR("{:#b}"),
|
||||
std::numeric_limits<int16_t>::min());
|
||||
check(STR("-0100000"), STR("{:#o}"), std::numeric_limits<int16_t>::min());
|
||||
check(STR("-32768"), STR("{:#}"), std::numeric_limits<int16_t>::min());
|
||||
check(STR("-0x8000"), STR("{:#x}"), std::numeric_limits<int16_t>::min());
|
||||
|
||||
check(STR("-0b10000000000000000000000000000000"), STR("{:#b}"),
|
||||
std::numeric_limits<int32_t>::min());
|
||||
check(STR("-020000000000"), STR("{:#o}"),
|
||||
std::numeric_limits<int32_t>::min());
|
||||
check(STR("-2147483648"), STR("{:#}"), std::numeric_limits<int32_t>::min());
|
||||
check(STR("-0x80000000"), STR("{:#x}"), std::numeric_limits<int32_t>::min());
|
||||
|
||||
check(STR("-0b100000000000000000000000000000000000000000000000000000000000000"
|
||||
"0"),
|
||||
STR("{:#b}"), std::numeric_limits<int64_t>::min());
|
||||
check(STR("-01000000000000000000000"), STR("{:#o}"),
|
||||
std::numeric_limits<int64_t>::min());
|
||||
check(STR("-9223372036854775808"), STR("{:#}"),
|
||||
std::numeric_limits<int64_t>::min());
|
||||
check(STR("-0x8000000000000000"), STR("{:#x}"),
|
||||
std::numeric_limits<int64_t>::min());
|
||||
|
||||
check(STR("0b1111111"), STR("{:#b}"), std::numeric_limits<int8_t>::max());
|
||||
check(STR("0177"), STR("{:#o}"), std::numeric_limits<int8_t>::max());
|
||||
check(STR("127"), STR("{:#}"), std::numeric_limits<int8_t>::max());
|
||||
check(STR("0x7f"), STR("{:#x}"), std::numeric_limits<int8_t>::max());
|
||||
|
||||
check(STR("0b111111111111111"), STR("{:#b}"),
|
||||
std::numeric_limits<int16_t>::max());
|
||||
check(STR("077777"), STR("{:#o}"), std::numeric_limits<int16_t>::max());
|
||||
check(STR("32767"), STR("{:#}"), std::numeric_limits<int16_t>::max());
|
||||
check(STR("0x7fff"), STR("{:#x}"), std::numeric_limits<int16_t>::max());
|
||||
|
||||
check(STR("0b1111111111111111111111111111111"), STR("{:#b}"),
|
||||
std::numeric_limits<int32_t>::max());
|
||||
check(STR("017777777777"), STR("{:#o}"), std::numeric_limits<int32_t>::max());
|
||||
check(STR("2147483647"), STR("{:#}"), std::numeric_limits<int32_t>::max());
|
||||
check(STR("0x7fffffff"), STR("{:#x}"), std::numeric_limits<int32_t>::max());
|
||||
|
||||
check(
|
||||
STR("0b111111111111111111111111111111111111111111111111111111111111111"),
|
||||
STR("{:#b}"), std::numeric_limits<int64_t>::max());
|
||||
check(STR("0777777777777777777777"), STR("{:#o}"),
|
||||
std::numeric_limits<int64_t>::max());
|
||||
check(STR("9223372036854775807"), STR("{:#}"),
|
||||
std::numeric_limits<int64_t>::max());
|
||||
check(STR("0x7fffffffffffffff"), STR("{:#x}"),
|
||||
std::numeric_limits<int64_t>::max());
|
||||
|
||||
// TODO FMT Add __int128_t test after implementing full range.
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_unsigned_integer(TestFunction check,
|
||||
ExceptionTest check_exception) {
|
||||
format_test_integer<unsigned char, CharT>(check, check_exception);
|
||||
format_test_integer<unsigned short, CharT>(check, check_exception);
|
||||
format_test_integer<unsigned, CharT>(check, check_exception);
|
||||
format_test_integer<unsigned long, CharT>(check, check_exception);
|
||||
format_test_integer<unsigned long long, CharT>(check, check_exception);
|
||||
#ifndef _LIBCPP_HAS_NO_INT128
|
||||
format_test_integer<__uint128_t, CharT>(check, check_exception);
|
||||
#endif
|
||||
// *** test the maxima ***
|
||||
check(STR("0b11111111"), STR("{:#b}"), std::numeric_limits<uint8_t>::max());
|
||||
check(STR("0377"), STR("{:#o}"), std::numeric_limits<uint8_t>::max());
|
||||
check(STR("255"), STR("{:#}"), std::numeric_limits<uint8_t>::max());
|
||||
check(STR("0xff"), STR("{:#x}"), std::numeric_limits<uint8_t>::max());
|
||||
|
||||
check(STR("0b1111111111111111"), STR("{:#b}"),
|
||||
std::numeric_limits<uint16_t>::max());
|
||||
check(STR("0177777"), STR("{:#o}"), std::numeric_limits<uint16_t>::max());
|
||||
check(STR("65535"), STR("{:#}"), std::numeric_limits<uint16_t>::max());
|
||||
check(STR("0xffff"), STR("{:#x}"), std::numeric_limits<uint16_t>::max());
|
||||
|
||||
check(STR("0b11111111111111111111111111111111"), STR("{:#b}"),
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
check(STR("037777777777"), STR("{:#o}"),
|
||||
std::numeric_limits<uint32_t>::max());
|
||||
check(STR("4294967295"), STR("{:#}"), std::numeric_limits<uint32_t>::max());
|
||||
check(STR("0xffffffff"), STR("{:#x}"), std::numeric_limits<uint32_t>::max());
|
||||
|
||||
check(
|
||||
STR("0b1111111111111111111111111111111111111111111111111111111111111111"),
|
||||
STR("{:#b}"), std::numeric_limits<uint64_t>::max());
|
||||
check(STR("01777777777777777777777"), STR("{:#o}"),
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
check(STR("18446744073709551615"), STR("{:#}"),
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
check(STR("0xffffffffffffffff"), STR("{:#x}"),
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
|
||||
// TODO FMT Add __uint128_t test after implementing full range.
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
// *** Test escaping ***
|
||||
@@ -349,6 +676,7 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
static_cast<__int128_t>(std::numeric_limits<long long>::max()) + 1);
|
||||
}
|
||||
#endif
|
||||
format_test_signed_integer<CharT>(check, check_exception);
|
||||
|
||||
// ** Test unsigned integral format argument ***
|
||||
check(STR("hello 42"), STR("hello {}"), static_cast<unsigned char>(42));
|
||||
@@ -372,6 +700,7 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
1);
|
||||
}
|
||||
#endif
|
||||
format_test_unsigned_integer<CharT>(check, check_exception);
|
||||
|
||||
// *** Test floating point format argument ***
|
||||
// TODO FMT Enable after floating-point support has been enabled
|
||||
|
||||
@@ -0,0 +1,568 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-localization
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// REQUIRES: locale.en_US.UTF-8
|
||||
|
||||
// <format>
|
||||
|
||||
// This test the locale-specific form for these formatting functions:
|
||||
//
|
||||
// // [format.functions], formatting functions
|
||||
// template<class... Args>
|
||||
// string format(string_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// wstring format(wstring_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// string format(const locale& loc, string_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// wstring format(const locale& loc, wstring_view fmt, const Args&... args);
|
||||
//
|
||||
// string vformat(string_view fmt, format_args args);
|
||||
// wstring vformat(wstring_view fmt, wformat_args args);
|
||||
// string vformat(const locale& loc, string_view fmt, format_args args);
|
||||
// wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);
|
||||
//
|
||||
// template<class Out, class... Args>
|
||||
// Out format_to(Out out, string_view fmt, const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// Out format_to(Out out, wstring_view fmt, const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// Out format_to(Out out, const locale& loc, string_view fmt, const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// Out format_to(Out out, const locale& loc, wstring_view fmt, const Args&... args);
|
||||
//
|
||||
// template<class Out>
|
||||
// Out vformat_to(Out out, string_view fmt,
|
||||
// format_args_t<type_identity_t<Out>, char> args);
|
||||
// template<class Out>
|
||||
// Out vformat_to(Out out, wstring_view fmt,
|
||||
// format_args_t<type_identity_t<Out>, wchar_t> args);
|
||||
// template<class Out>
|
||||
// Out vformat_to(Out out, const locale& loc, string_view fmt,
|
||||
// format_args_t<type_identity_t<Out>, char> args);
|
||||
// template<class Out>
|
||||
// Out vformat_to(Out out, const locale& loc, wstring_view fmt,
|
||||
// format_args_t<type_identity_t<Out>, wchar_t> args);
|
||||
//
|
||||
// template<class Out> struct format_to_n_result {
|
||||
// Out out;
|
||||
// iter_difference_t<Out> size;
|
||||
// };
|
||||
//
|
||||
// template<class Out, class... Args>
|
||||
// format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
// string_view fmt, const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
// wstring_view fmt, const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
// const locale& loc, string_view fmt,
|
||||
// const Args&... args);
|
||||
// template<class Out, class... Args>
|
||||
// format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
||||
// const locale& loc, wstring_view fmt,
|
||||
// const Args&... args);
|
||||
//
|
||||
// template<class... Args>
|
||||
// size_t formatted_size(string_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// size_t formatted_size(wstring_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
|
||||
// template<class... Args>
|
||||
// size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "make_string.h"
|
||||
#include "platform_support.h" // locale name macros
|
||||
|
||||
#define STR(S) MAKE_STRING(CharT, S)
|
||||
|
||||
template <class CharT>
|
||||
struct numpunct;
|
||||
|
||||
template <>
|
||||
struct numpunct<char> : std::numpunct<char> {
|
||||
string_type do_truename() const override { return "yes"; }
|
||||
string_type do_falsename() const override { return "no"; }
|
||||
|
||||
std::string do_grouping() const override { return "\1\2\3\2\1"; };
|
||||
char do_thousands_sep() const override { return '_'; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct numpunct<wchar_t> : std::numpunct<wchar_t> {
|
||||
string_type do_truename() const override { return L"yes"; }
|
||||
string_type do_falsename() const override { return L"no"; }
|
||||
|
||||
std::string do_grouping() const override { return "\1\2\3\2\1"; };
|
||||
wchar_t do_thousands_sep() const override { return L'_'; }
|
||||
};
|
||||
|
||||
template <class CharT, class... Args>
|
||||
void test(std::basic_string<CharT> expected, std::basic_string<CharT> fmt,
|
||||
const Args&... args) {
|
||||
// *** format ***
|
||||
{
|
||||
std::basic_string<CharT> out = std::format(fmt, args...);
|
||||
if constexpr (std::same_as<CharT, char>)
|
||||
if (out != expected)
|
||||
std::cerr << "\nFormat string " << fmt << "\nExpected output "
|
||||
<< expected << "\nActual output " << out << '\n';
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** vformat ***
|
||||
{
|
||||
std::basic_string<CharT> out = std::vformat(
|
||||
fmt, std::make_format_args<std::basic_format_context<
|
||||
std::back_insert_iterator<std::basic_string<CharT>>, CharT>>(
|
||||
args...));
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** format_to ***
|
||||
{
|
||||
std::basic_string<CharT> out(expected.size(), CharT(' '));
|
||||
auto it = std::format_to(out.begin(), fmt, args...);
|
||||
assert(it == out.end());
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** vformat_to ***
|
||||
{
|
||||
std::basic_string<CharT> out(expected.size(), CharT(' '));
|
||||
auto it = std::vformat_to(
|
||||
out.begin(), fmt,
|
||||
std::make_format_args<std::basic_format_context<
|
||||
typename std::basic_string<CharT>::iterator, CharT>>(args...));
|
||||
assert(it == out.end());
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** format_to_n ***
|
||||
{
|
||||
std::basic_string<CharT> out;
|
||||
std::format_to_n_result result =
|
||||
std::format_to_n(std::back_inserter(out), 1000, fmt, args...);
|
||||
using diff_type = decltype(result.size);
|
||||
diff_type formatted_size = std::formatted_size(fmt, args...);
|
||||
diff_type size = std::min<diff_type>(1000, formatted_size);
|
||||
|
||||
assert(result.size == formatted_size);
|
||||
assert(out == expected.substr(0, size));
|
||||
}
|
||||
// *** formatted_size ***
|
||||
{
|
||||
size_t size = std::formatted_size(fmt, args...);
|
||||
assert(size == expected.size());
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT, class... Args>
|
||||
void test(std::basic_string<CharT> expected, std::locale loc,
|
||||
std::basic_string<CharT> fmt, const Args&... args) {
|
||||
// *** format ***
|
||||
{
|
||||
std::basic_string<CharT> out = std::format(loc, fmt, args...);
|
||||
if constexpr (std::same_as<CharT, char>)
|
||||
if (out != expected)
|
||||
std::cerr << "\nFormat string " << fmt << "\nExpected output "
|
||||
<< expected << "\nActual output " << out << '\n';
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** vformat ***
|
||||
{
|
||||
std::basic_string<CharT> out = std::vformat(
|
||||
loc, fmt,
|
||||
std::make_format_args<std::basic_format_context<
|
||||
std::back_insert_iterator<std::basic_string<CharT>>, CharT>>(
|
||||
args...));
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** format_to ***
|
||||
{
|
||||
std::basic_string<CharT> out(expected.size(), CharT(' '));
|
||||
auto it = std::format_to(out.begin(), loc, fmt, args...);
|
||||
assert(it == out.end());
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** vformat_to ***
|
||||
{
|
||||
std::basic_string<CharT> out(expected.size(), CharT(' '));
|
||||
auto it = std::vformat_to(
|
||||
out.begin(), loc, fmt,
|
||||
std::make_format_args<std::basic_format_context<
|
||||
typename std::basic_string<CharT>::iterator, CharT>>(args...));
|
||||
assert(it == out.end());
|
||||
assert(out == expected);
|
||||
}
|
||||
// *** format_to_n ***
|
||||
{
|
||||
std::basic_string<CharT> out;
|
||||
std::format_to_n_result result =
|
||||
std::format_to_n(std::back_inserter(out), 1000, loc, fmt, args...);
|
||||
using diff_type = decltype(result.size);
|
||||
diff_type formatted_size = std::formatted_size(loc, fmt, args...);
|
||||
diff_type size = std::min<diff_type>(1000, formatted_size);
|
||||
|
||||
assert(result.size == formatted_size);
|
||||
assert(out == expected.substr(0, size));
|
||||
}
|
||||
// *** formatted_size ***
|
||||
{
|
||||
size_t size = std::formatted_size(loc, fmt, args...);
|
||||
assert(size == expected.size());
|
||||
}
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
void test_integer() {
|
||||
std::locale loc = std::locale(std::locale(), new numpunct<CharT>());
|
||||
std::locale en_US = std::locale(LOCALE_en_US_UTF_8);
|
||||
|
||||
// *** Decimal ***
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), STR("{:L}"), 0);
|
||||
test(STR("1"), STR("{:L}"), 1);
|
||||
test(STR("10"), STR("{:L}"), 10);
|
||||
test(STR("100"), STR("{:L}"), 100);
|
||||
test(STR("1,000"), STR("{:L}"), 1'000);
|
||||
test(STR("10,000"), STR("{:L}"), 10'000);
|
||||
test(STR("100,000"), STR("{:L}"), 100'000);
|
||||
test(STR("1,000,000"), STR("{:L}"), 1'000'000);
|
||||
test(STR("10,000,000"), STR("{:L}"), 10'000'000);
|
||||
test(STR("100,000,000"), STR("{:L}"), 100'000'000);
|
||||
test(STR("1,000,000,000"), STR("{:L}"), 1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:L}"), -1);
|
||||
test(STR("-10"), STR("{:L}"), -10);
|
||||
test(STR("-100"), STR("{:L}"), -100);
|
||||
test(STR("-1,000"), STR("{:L}"), -1'000);
|
||||
test(STR("-10,000"), STR("{:L}"), -10'000);
|
||||
test(STR("-100,000"), STR("{:L}"), -100'000);
|
||||
test(STR("-1,000,000"), STR("{:L}"), -1'000'000);
|
||||
test(STR("-10,000,000"), STR("{:L}"), -10'000'000);
|
||||
test(STR("-100,000,000"), STR("{:L}"), -100'000'000);
|
||||
test(STR("-1,000,000,000"), STR("{:L}"), -1'000'000'000);
|
||||
|
||||
std::locale::global(loc);
|
||||
test(STR("0"), STR("{:L}"), 0);
|
||||
test(STR("1"), STR("{:L}"), 1);
|
||||
test(STR("1_0"), STR("{:L}"), 10);
|
||||
test(STR("10_0"), STR("{:L}"), 100);
|
||||
test(STR("1_00_0"), STR("{:L}"), 1'000);
|
||||
test(STR("10_00_0"), STR("{:L}"), 10'000);
|
||||
test(STR("100_00_0"), STR("{:L}"), 100'000);
|
||||
test(STR("1_000_00_0"), STR("{:L}"), 1'000'000);
|
||||
test(STR("10_000_00_0"), STR("{:L}"), 10'000'000);
|
||||
test(STR("1_00_000_00_0"), STR("{:L}"), 100'000'000);
|
||||
test(STR("1_0_00_000_00_0"), STR("{:L}"), 1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:L}"), -1);
|
||||
test(STR("-1_0"), STR("{:L}"), -10);
|
||||
test(STR("-10_0"), STR("{:L}"), -100);
|
||||
test(STR("-1_00_0"), STR("{:L}"), -1'000);
|
||||
test(STR("-10_00_0"), STR("{:L}"), -10'000);
|
||||
test(STR("-100_00_0"), STR("{:L}"), -100'000);
|
||||
test(STR("-1_000_00_0"), STR("{:L}"), -1'000'000);
|
||||
test(STR("-10_000_00_0"), STR("{:L}"), -10'000'000);
|
||||
test(STR("-1_00_000_00_0"), STR("{:L}"), -100'000'000);
|
||||
test(STR("-1_0_00_000_00_0"), STR("{:L}"), -1'000'000'000);
|
||||
|
||||
test(STR("0"), en_US, STR("{:L}"), 0);
|
||||
test(STR("1"), en_US, STR("{:L}"), 1);
|
||||
test(STR("10"), en_US, STR("{:L}"), 10);
|
||||
test(STR("100"), en_US, STR("{:L}"), 100);
|
||||
test(STR("1,000"), en_US, STR("{:L}"), 1'000);
|
||||
test(STR("10,000"), en_US, STR("{:L}"), 10'000);
|
||||
test(STR("100,000"), en_US, STR("{:L}"), 100'000);
|
||||
test(STR("1,000,000"), en_US, STR("{:L}"), 1'000'000);
|
||||
test(STR("10,000,000"), en_US, STR("{:L}"), 10'000'000);
|
||||
test(STR("100,000,000"), en_US, STR("{:L}"), 100'000'000);
|
||||
test(STR("1,000,000,000"), en_US, STR("{:L}"), 1'000'000'000);
|
||||
|
||||
test(STR("-1"), en_US, STR("{:L}"), -1);
|
||||
test(STR("-10"), en_US, STR("{:L}"), -10);
|
||||
test(STR("-100"), en_US, STR("{:L}"), -100);
|
||||
test(STR("-1,000"), en_US, STR("{:L}"), -1'000);
|
||||
test(STR("-10,000"), en_US, STR("{:L}"), -10'000);
|
||||
test(STR("-100,000"), en_US, STR("{:L}"), -100'000);
|
||||
test(STR("-1,000,000"), en_US, STR("{:L}"), -1'000'000);
|
||||
test(STR("-10,000,000"), en_US, STR("{:L}"), -10'000'000);
|
||||
test(STR("-100,000,000"), en_US, STR("{:L}"), -100'000'000);
|
||||
test(STR("-1,000,000,000"), en_US, STR("{:L}"), -1'000'000'000);
|
||||
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), loc, STR("{:L}"), 0);
|
||||
test(STR("1"), loc, STR("{:L}"), 1);
|
||||
test(STR("1_0"), loc, STR("{:L}"), 10);
|
||||
test(STR("10_0"), loc, STR("{:L}"), 100);
|
||||
test(STR("1_00_0"), loc, STR("{:L}"), 1'000);
|
||||
test(STR("10_00_0"), loc, STR("{:L}"), 10'000);
|
||||
test(STR("100_00_0"), loc, STR("{:L}"), 100'000);
|
||||
test(STR("1_000_00_0"), loc, STR("{:L}"), 1'000'000);
|
||||
test(STR("10_000_00_0"), loc, STR("{:L}"), 10'000'000);
|
||||
test(STR("1_00_000_00_0"), loc, STR("{:L}"), 100'000'000);
|
||||
test(STR("1_0_00_000_00_0"), loc, STR("{:L}"), 1'000'000'000);
|
||||
|
||||
test(STR("-1"), loc, STR("{:L}"), -1);
|
||||
test(STR("-1_0"), loc, STR("{:L}"), -10);
|
||||
test(STR("-10_0"), loc, STR("{:L}"), -100);
|
||||
test(STR("-1_00_0"), loc, STR("{:L}"), -1'000);
|
||||
test(STR("-10_00_0"), loc, STR("{:L}"), -10'000);
|
||||
test(STR("-100_00_0"), loc, STR("{:L}"), -100'000);
|
||||
test(STR("-1_000_00_0"), loc, STR("{:L}"), -1'000'000);
|
||||
test(STR("-10_000_00_0"), loc, STR("{:L}"), -10'000'000);
|
||||
test(STR("-1_00_000_00_0"), loc, STR("{:L}"), -100'000'000);
|
||||
test(STR("-1_0_00_000_00_0"), loc, STR("{:L}"), -1'000'000'000);
|
||||
|
||||
// *** Binary ***
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), STR("{:Lb}"), 0b0);
|
||||
test(STR("1"), STR("{:Lb}"), 0b1);
|
||||
test(STR("1,000,000,000"), STR("{:Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("0b0"), STR("{:#Lb}"), 0b0);
|
||||
test(STR("0b1"), STR("{:#Lb}"), 0b1);
|
||||
test(STR("0b1,000,000,000"), STR("{:#Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:LB}"), -0b1);
|
||||
test(STR("-1,000,000,000"), STR("{:LB}"), -0b1'000'000'000);
|
||||
|
||||
test(STR("-0B1"), STR("{:#LB}"), -0b1);
|
||||
test(STR("-0B1,000,000,000"), STR("{:#LB}"), -0b1'000'000'000);
|
||||
|
||||
std::locale::global(loc);
|
||||
test(STR("0"), STR("{:Lb}"), 0b0);
|
||||
test(STR("1"), STR("{:Lb}"), 0b1);
|
||||
test(STR("1_0_00_000_00_0"), STR("{:Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("0b0"), STR("{:#Lb}"), 0b0);
|
||||
test(STR("0b1"), STR("{:#Lb}"), 0b1);
|
||||
test(STR("0b1_0_00_000_00_0"), STR("{:#Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:LB}"), -0b1);
|
||||
test(STR("-1_0_00_000_00_0"), STR("{:LB}"), -0b1'000'000'000);
|
||||
|
||||
test(STR("-0B1"), STR("{:#LB}"), -0b1);
|
||||
test(STR("-0B1_0_00_000_00_0"), STR("{:#LB}"), -0b1'000'000'000);
|
||||
|
||||
test(STR("0"), en_US, STR("{:Lb}"), 0b0);
|
||||
test(STR("1"), en_US, STR("{:Lb}"), 0b1);
|
||||
test(STR("1,000,000,000"), en_US, STR("{:Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("0b0"), en_US, STR("{:#Lb}"), 0b0);
|
||||
test(STR("0b1"), en_US, STR("{:#Lb}"), 0b1);
|
||||
test(STR("0b1,000,000,000"), en_US, STR("{:#Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("-1"), en_US, STR("{:LB}"), -0b1);
|
||||
test(STR("-1,000,000,000"), en_US, STR("{:LB}"), -0b1'000'000'000);
|
||||
|
||||
test(STR("-0B1"), en_US, STR("{:#LB}"), -0b1);
|
||||
test(STR("-0B1,000,000,000"), en_US, STR("{:#LB}"), -0b1'000'000'000);
|
||||
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), loc, STR("{:Lb}"), 0b0);
|
||||
test(STR("1"), loc, STR("{:Lb}"), 0b1);
|
||||
test(STR("1_0_00_000_00_0"), loc, STR("{:Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("0b0"), loc, STR("{:#Lb}"), 0b0);
|
||||
test(STR("0b1"), loc, STR("{:#Lb}"), 0b1);
|
||||
test(STR("0b1_0_00_000_00_0"), loc, STR("{:#Lb}"), 0b1'000'000'000);
|
||||
|
||||
test(STR("-1"), loc, STR("{:LB}"), -0b1);
|
||||
test(STR("-1_0_00_000_00_0"), loc, STR("{:LB}"), -0b1'000'000'000);
|
||||
|
||||
test(STR("-0B1"), loc, STR("{:#LB}"), -0b1);
|
||||
test(STR("-0B1_0_00_000_00_0"), loc, STR("{:#LB}"), -0b1'000'000'000);
|
||||
|
||||
// *** Octal ***
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), STR("{:Lo}"), 00);
|
||||
test(STR("1"), STR("{:Lo}"), 01);
|
||||
test(STR("1,000,000,000"), STR("{:Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("0"), STR("{:#Lo}"), 00);
|
||||
test(STR("01"), STR("{:#Lo}"), 01);
|
||||
test(STR("01,000,000,000"), STR("{:#Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:Lo}"), -01);
|
||||
test(STR("-1,000,000,000"), STR("{:Lo}"), -01'000'000'000);
|
||||
|
||||
test(STR("-01"), STR("{:#Lo}"), -01);
|
||||
test(STR("-01,000,000,000"), STR("{:#Lo}"), -01'000'000'000);
|
||||
|
||||
std::locale::global(loc);
|
||||
test(STR("0"), STR("{:Lo}"), 00);
|
||||
test(STR("1"), STR("{:Lo}"), 01);
|
||||
test(STR("1_0_00_000_00_0"), STR("{:Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("0"), STR("{:#Lo}"), 00);
|
||||
test(STR("01"), STR("{:#Lo}"), 01);
|
||||
test(STR("01_0_00_000_00_0"), STR("{:#Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:Lo}"), -01);
|
||||
test(STR("-1_0_00_000_00_0"), STR("{:Lo}"), -01'000'000'000);
|
||||
|
||||
test(STR("-01"), STR("{:#Lo}"), -01);
|
||||
test(STR("-01_0_00_000_00_0"), STR("{:#Lo}"), -01'000'000'000);
|
||||
|
||||
test(STR("0"), en_US, STR("{:Lo}"), 00);
|
||||
test(STR("1"), en_US, STR("{:Lo}"), 01);
|
||||
test(STR("1,000,000,000"), en_US, STR("{:Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("0"), en_US, STR("{:#Lo}"), 00);
|
||||
test(STR("01"), en_US, STR("{:#Lo}"), 01);
|
||||
test(STR("01,000,000,000"), en_US, STR("{:#Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("-1"), en_US, STR("{:Lo}"), -01);
|
||||
test(STR("-1,000,000,000"), en_US, STR("{:Lo}"), -01'000'000'000);
|
||||
|
||||
test(STR("-01"), en_US, STR("{:#Lo}"), -01);
|
||||
test(STR("-01,000,000,000"), en_US, STR("{:#Lo}"), -01'000'000'000);
|
||||
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), loc, STR("{:Lo}"), 00);
|
||||
test(STR("1"), loc, STR("{:Lo}"), 01);
|
||||
test(STR("1_0_00_000_00_0"), loc, STR("{:Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("0"), loc, STR("{:#Lo}"), 00);
|
||||
test(STR("01"), loc, STR("{:#Lo}"), 01);
|
||||
test(STR("01_0_00_000_00_0"), loc, STR("{:#Lo}"), 01'000'000'000);
|
||||
|
||||
test(STR("-1"), loc, STR("{:Lo}"), -01);
|
||||
test(STR("-1_0_00_000_00_0"), loc, STR("{:Lo}"), -01'000'000'000);
|
||||
|
||||
test(STR("-01"), loc, STR("{:#Lo}"), -01);
|
||||
test(STR("-01_0_00_000_00_0"), loc, STR("{:#Lo}"), -01'000'000'000);
|
||||
|
||||
// *** Hexadecimal ***
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), STR("{:Lx}"), 0x0);
|
||||
test(STR("1"), STR("{:Lx}"), 0x1);
|
||||
test(STR("1,000,000,000"), STR("{:Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("0x0"), STR("{:#Lx}"), 0x0);
|
||||
test(STR("0x1"), STR("{:#Lx}"), 0x1);
|
||||
test(STR("0x1,000,000,000"), STR("{:#Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:LX}"), -0x1);
|
||||
test(STR("-1,000,000,000"), STR("{:LX}"), -0x1'000'000'000);
|
||||
|
||||
test(STR("-0X1"), STR("{:#LX}"), -0x1);
|
||||
test(STR("-0X1,000,000,000"), STR("{:#LX}"), -0x1'000'000'000);
|
||||
|
||||
std::locale::global(loc);
|
||||
test(STR("0"), STR("{:Lx}"), 0x0);
|
||||
test(STR("1"), STR("{:Lx}"), 0x1);
|
||||
test(STR("1_0_00_000_00_0"), STR("{:Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("0x0"), STR("{:#Lx}"), 0x0);
|
||||
test(STR("0x1"), STR("{:#Lx}"), 0x1);
|
||||
test(STR("0x1_0_00_000_00_0"), STR("{:#Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("-1"), STR("{:LX}"), -0x1);
|
||||
test(STR("-1_0_00_000_00_0"), STR("{:LX}"), -0x1'000'000'000);
|
||||
|
||||
test(STR("-0X1"), STR("{:#LX}"), -0x1);
|
||||
test(STR("-0X1_0_00_000_00_0"), STR("{:#LX}"), -0x1'000'000'000);
|
||||
|
||||
test(STR("0"), en_US, STR("{:Lx}"), 0x0);
|
||||
test(STR("1"), en_US, STR("{:Lx}"), 0x1);
|
||||
test(STR("1,000,000,000"), en_US, STR("{:Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("0x0"), en_US, STR("{:#Lx}"), 0x0);
|
||||
test(STR("0x1"), en_US, STR("{:#Lx}"), 0x1);
|
||||
test(STR("0x1,000,000,000"), en_US, STR("{:#Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("-1"), en_US, STR("{:LX}"), -0x1);
|
||||
test(STR("-1,000,000,000"), en_US, STR("{:LX}"), -0x1'000'000'000);
|
||||
|
||||
test(STR("-0X1"), en_US, STR("{:#LX}"), -0x1);
|
||||
test(STR("-0X1,000,000,000"), en_US, STR("{:#LX}"), -0x1'000'000'000);
|
||||
|
||||
std::locale::global(en_US);
|
||||
test(STR("0"), loc, STR("{:Lx}"), 0x0);
|
||||
test(STR("1"), loc, STR("{:Lx}"), 0x1);
|
||||
test(STR("1_0_00_000_00_0"), loc, STR("{:Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("0x0"), loc, STR("{:#Lx}"), 0x0);
|
||||
test(STR("0x1"), loc, STR("{:#Lx}"), 0x1);
|
||||
test(STR("0x1_0_00_000_00_0"), loc, STR("{:#Lx}"), 0x1'000'000'000);
|
||||
|
||||
test(STR("-1"), loc, STR("{:LX}"), -0x1);
|
||||
test(STR("-1_0_00_000_00_0"), loc, STR("{:LX}"), -0x1'000'000'000);
|
||||
|
||||
test(STR("-0X1"), loc, STR("{:#LX}"), -0x1);
|
||||
test(STR("-0X1_0_00_000_00_0"), loc, STR("{:#LX}"), -0x1'000'000'000);
|
||||
|
||||
// *** align-fill & width ***
|
||||
test(STR("4_2"), loc, STR("{:L}"), 42);
|
||||
|
||||
test(STR(" 4_2"), loc, STR("{:6L}"), 42);
|
||||
test(STR("4_2 "), loc, STR("{:<6L}"), 42);
|
||||
test(STR(" 4_2 "), loc, STR("{:^6L}"), 42);
|
||||
test(STR(" 4_2"), loc, STR("{:>6L}"), 42);
|
||||
|
||||
test(STR("4_2***"), loc, STR("{:*<6L}"), 42);
|
||||
test(STR("*4_2**"), loc, STR("{:*^6L}"), 42);
|
||||
test(STR("***4_2"), loc, STR("{:*>6L}"), 42);
|
||||
|
||||
test(STR("4_a*****"), loc, STR("{:*<8Lx}"), 0x4a);
|
||||
test(STR("**4_a***"), loc, STR("{:*^8Lx}"), 0x4a);
|
||||
test(STR("*****4_a"), loc, STR("{:*>8Lx}"), 0x4a);
|
||||
|
||||
test(STR("0x4_a***"), loc, STR("{:*<#8Lx}"), 0x4a);
|
||||
test(STR("*0x4_a**"), loc, STR("{:*^#8Lx}"), 0x4a);
|
||||
test(STR("***0x4_a"), loc, STR("{:*>#8Lx}"), 0x4a);
|
||||
|
||||
test(STR("4_A*****"), loc, STR("{:*<8LX}"), 0x4a);
|
||||
test(STR("**4_A***"), loc, STR("{:*^8LX}"), 0x4a);
|
||||
test(STR("*****4_A"), loc, STR("{:*>8LX}"), 0x4a);
|
||||
|
||||
test(STR("0X4_A***"), loc, STR("{:*<#8LX}"), 0x4a);
|
||||
test(STR("*0X4_A**"), loc, STR("{:*^#8LX}"), 0x4a);
|
||||
test(STR("***0X4_A"), loc, STR("{:*>#8LX}"), 0x4a);
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
test(STR("4_2 "), loc, STR("{:<06L}"), 42);
|
||||
test(STR(" 4_2 "), loc, STR("{:^06L}"), 42);
|
||||
test(STR(" 4_2"), loc, STR("{:>06L}"), 42);
|
||||
|
||||
// *** zero-padding & width ***
|
||||
test(STR(" 4_2"), loc, STR("{:6L}"), 42);
|
||||
test(STR("0004_2"), loc, STR("{:06L}"), 42);
|
||||
test(STR("-004_2"), loc, STR("{:06L}"), -42);
|
||||
|
||||
test(STR("000004_a"), loc, STR("{:08Lx}"), 0x4a);
|
||||
test(STR("0x0004_a"), loc, STR("{:#08Lx}"), 0x4a);
|
||||
test(STR("0X0004_A"), loc, STR("{:#08LX}"), 0x4a);
|
||||
|
||||
test(STR("-00004_a"), loc, STR("{:08Lx}"), -0x4a);
|
||||
test(STR("-0x004_a"), loc, STR("{:#08Lx}"), -0x4a);
|
||||
test(STR("-0X004_A"), loc, STR("{:#08LX}"), -0x4a);
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
void test() {
|
||||
test_integer<CharT>();
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
test<wchar_t>();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user