[libc++][format] Adds char formatter.
Implements the formatter for all fundamental integer types. [format.formatter.spec]/2.1 The specializations ``` template<> struct formatter<char, char>; template<> struct formatter<char, wchar_t>; template<> struct formatter<wchar_t, wchar_t>; ``` This removes the stub implemented in D96664. Implements parts of: - P0645 Text Formatting Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D103466
This commit is contained in:
@@ -140,6 +140,7 @@ set(files
|
||||
__format/format_parse_context.h
|
||||
__format/format_string.h
|
||||
__format/formatter.h
|
||||
__format/formatter_char.h
|
||||
__format/formatter_integer.h
|
||||
__format/formatter_integral.h
|
||||
__format/formatter_string.h
|
||||
|
||||
103
libcxx/include/__format/formatter_char.h
Normal file
103
libcxx/include/__format/formatter_char.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// -*- 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_CHAR_H
|
||||
#define _LIBCPP___FORMAT_FORMATTER_CHAR_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>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_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_char : 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::__char;
|
||||
[[fallthrough]];
|
||||
case _Flags::_Type::__char:
|
||||
this->__handle_char();
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
__throw_format_error(
|
||||
"The format-spec type has a type not supported for a char argument");
|
||||
}
|
||||
|
||||
return __it;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _CharT>
|
||||
using __formatter_char = __formatter_integral<__parser_char<_CharT>>;
|
||||
|
||||
} // namespace __format_spec
|
||||
|
||||
// [format.formatter.spec]/2.1 The specializations
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<char, char>
|
||||
: public __format_spec::__formatter_char<char> {};
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<char, wchar_t>
|
||||
: public __format_spec::__formatter_char<wchar_t> {
|
||||
using _Base = __format_spec::__formatter_char<wchar_t>;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI auto format(char __value, auto& __ctx)
|
||||
-> decltype(__ctx.out()) {
|
||||
return _Base::format(static_cast<wchar_t>(__value), __ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
||||
formatter<wchar_t, wchar_t>
|
||||
: public __format_spec::__formatter_char<wchar_t> {};
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
#endif //_LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FORMAT_FORMATTER_CHAR_H
|
||||
@@ -279,6 +279,7 @@ namespace std {
|
||||
#include <__format/format_parse_context.h>
|
||||
#include <__format/format_string.h>
|
||||
#include <__format/formatter.h>
|
||||
#include <__format/formatter_char.h>
|
||||
#include <__format/formatter_integer.h>
|
||||
#include <__format/formatter_string.h>
|
||||
#include <__format/parser_std_format_spec.h>
|
||||
@@ -338,24 +339,7 @@ _LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
|
||||
make_wformat_args(const _Args&... __args) {
|
||||
return _VSTD::make_format_args<wformat_context>(__args...);
|
||||
}
|
||||
|
||||
namespace __format {
|
||||
template <class _Tp, class _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS __formatter_char {
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
||||
// TODO FMT Implement this function.
|
||||
return __parse_ctx.begin();
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI
|
||||
auto format(_Tp __c, auto& __ctx) -> decltype(__ctx.out()) {
|
||||
// TODO FMT Implement the parsed formatting arguments.
|
||||
auto __out_it = __ctx.out();
|
||||
*__out_it++ = _CharT(__c);
|
||||
return __out_it;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Tp, class _CharT>
|
||||
requires(is_arithmetic_v<_Tp> &&
|
||||
@@ -405,20 +389,6 @@ private:
|
||||
// These specializations are helper stubs and not proper formatters.
|
||||
// TODO FMT Implement the proper formatter specializations.
|
||||
|
||||
// [format.formatter.spec]/2.1 The specializations
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<char, char>
|
||||
: public __format::__formatter_char<char, char> {};
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<char, wchar_t>
|
||||
: public __format::__formatter_char<char, wchar_t> {};
|
||||
|
||||
template <>
|
||||
struct _LIBCPP_TEMPLATE_VIS formatter<wchar_t, wchar_t>
|
||||
: public __format::__formatter_char<wchar_t, wchar_t> {};
|
||||
|
||||
// [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
|
||||
|
||||
@@ -450,6 +450,7 @@ 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_char { private header "__format/formatter_char.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" }
|
||||
|
||||
@@ -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_char.h'}}
|
||||
#include <__format/formatter_char.h>
|
||||
@@ -0,0 +1,452 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// <format>
|
||||
|
||||
// Tests the parsing of the format string as specified in [format.string.std].
|
||||
// It validates whether the std-format-spec is valid for a char 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_char<CharT>;
|
||||
|
||||
template <class CharT>
|
||||
struct Expected {
|
||||
CharT fill = CharT(' ');
|
||||
_Flags::_Alignment alignment = _Flags::_Alignment::__left;
|
||||
_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::__char;
|
||||
};
|
||||
|
||||
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.__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_as_char() {
|
||||
|
||||
test({}, 1, CSTR("c}"));
|
||||
|
||||
// *** Align-fill ***
|
||||
test({.alignment = _Flags::_Alignment::__left}, 1, CSTR("<}"));
|
||||
test({.alignment = _Flags::_Alignment::__center}, 1, "^}");
|
||||
test({.alignment = _Flags::_Alignment::__right}, 1, ">}");
|
||||
|
||||
test({.alignment = _Flags::_Alignment::__left}, 2, CSTR("<c}"));
|
||||
test({.alignment = _Flags::_Alignment::__center}, 2, "^c}");
|
||||
test({.alignment = _Flags::_Alignment::__right}, 2, ">c}");
|
||||
|
||||
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({.fill = CharT('L'), .alignment = _Flags::_Alignment::__left}, 3,
|
||||
CSTR("L<c}"));
|
||||
test({.fill = CharT('#'), .alignment = _Flags::_Alignment::__center}, 3,
|
||||
CSTR("#^c}"));
|
||||
test({.fill = CharT('0'), .alignment = _Flags::_Alignment::__right}, 3,
|
||||
CSTR("0>c}"));
|
||||
|
||||
// *** Sign ***
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR("-"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR("+"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A sign field isn't allowed in this format-spec", CSTR(" "));
|
||||
|
||||
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_exception<Parser<CharT>>(
|
||||
"An alternate form field isn't allowed in this format-spec", CSTR("#}"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"An alternate form field isn't allowed in this format-spec", CSTR("#c}"));
|
||||
|
||||
// *** Zero padding ***
|
||||
test_exception<Parser<CharT>>(
|
||||
"A zero-padding field isn't allowed in this format-spec", CSTR("0}"));
|
||||
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("}"));
|
||||
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>>(
|
||||
"A format-spec arg-id should terminate at a '}'", CSTR("{0"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"The arg-id of the format-spec starts with an invalid character",
|
||||
CSTR("{a"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A format-spec arg-id should terminate at a '}'", CSTR("{1"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A format-spec arg-id should terminate at a '}'", CSTR("{9"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A format-spec arg-id should terminate at a '}'", CSTR("{9:"));
|
||||
test_exception<Parser<CharT>>(
|
||||
"A format-spec arg-id should terminate at a '}'", 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_exception<Parser<CharT>>(
|
||||
"The format-spec should consume the input or end with a '}'", CSTR("."));
|
||||
test_exception<Parser<CharT>>(
|
||||
"The format-spec should consume the input or end with a '}'", CSTR(".1"));
|
||||
|
||||
// *** Locale-specific form ***
|
||||
// Note the flag is allowed, but has no effect.
|
||||
test({.locale_specific_form = true}, 1, CSTR("L}"));
|
||||
test({.locale_specific_form = true}, 2, CSTR("Lc}"));
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr void test_as_integer() {
|
||||
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
1, CSTR("d}"));
|
||||
|
||||
// *** Align-fill ***
|
||||
test({.alignment = _Flags::_Alignment::__left,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("<d}"));
|
||||
test({.alignment = _Flags::_Alignment::__center,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, "^d}");
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, ">d}");
|
||||
|
||||
test({.fill = CharT('L'),
|
||||
.alignment = _Flags::_Alignment::__left,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("L<d}"));
|
||||
test({.fill = CharT('#'),
|
||||
.alignment = _Flags::_Alignment::__center,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("#^d}"));
|
||||
test({.fill = CharT('0'),
|
||||
.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("0>d}"));
|
||||
|
||||
// *** Sign ***
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.sign = _Flags::_Sign::__minus,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("-d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.sign = _Flags::_Sign::__plus,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("+d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.sign = _Flags::_Sign::__space,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR(" d}"));
|
||||
|
||||
// *** Alternate form ***
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.alternate_form = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("#d}"));
|
||||
|
||||
// *** Zero padding ***
|
||||
test({.alignment = _Flags::_Alignment::__default,
|
||||
.zero_padding = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("0d}"));
|
||||
test({.alignment = _Flags::_Alignment::__center,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("^0d}"));
|
||||
|
||||
// *** Width ***
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 0,
|
||||
.width_as_arg = false,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
1, CSTR("d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 1,
|
||||
.width_as_arg = false,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("1d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 10,
|
||||
.width_as_arg = false,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("10d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 1000,
|
||||
.width_as_arg = false,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
5, CSTR("1000d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 1000000,
|
||||
.width_as_arg = false,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
8, CSTR("1000000d}"));
|
||||
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 0,
|
||||
.width_as_arg = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
3, CSTR("{}d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 0,
|
||||
.width_as_arg = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
4, CSTR("{0}d}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.width = 1,
|
||||
.width_as_arg = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
4, CSTR("{1}d}"));
|
||||
|
||||
// *** Precision ***
|
||||
test_exception<Parser<CharT>>(
|
||||
"The format-spec should consume the input or end with a '}'", CSTR("."));
|
||||
test_exception<Parser<CharT>>(
|
||||
"The format-spec should consume the input or end with a '}'", CSTR(".1"));
|
||||
|
||||
// *** Locale-specific form ***
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.locale_specific_form = true,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
2, CSTR("Ld}"));
|
||||
}
|
||||
|
||||
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);
|
||||
static_assert(!has_precision<decltype(parser)>);
|
||||
static_assert(!has_precision_as_arg<decltype(parser)>);
|
||||
assert(parser.__locale_specific_form == false);
|
||||
assert(parser.__type == _Flags::_Type::__default);
|
||||
|
||||
test({}, 0, CSTR("}"));
|
||||
|
||||
test_as_char<CharT>();
|
||||
test_as_integer<CharT>();
|
||||
|
||||
// *** Type ***
|
||||
{
|
||||
const char* unsuported_type =
|
||||
"The format-spec type has a type not supported for a char argument";
|
||||
const char* not_a_type =
|
||||
"The format-spec should consume the input or end with a '}'";
|
||||
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("A}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.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_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}"));
|
||||
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({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__hexadecimal_upper_case},
|
||||
1, CSTR("X}"));
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("Y}"));
|
||||
test_exception<Parser<CharT>>(not_a_type, CSTR("Z}"));
|
||||
|
||||
test_exception<Parser<CharT>>(unsuported_type, CSTR("a}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__binary_lower_case},
|
||||
1, CSTR("b}"));
|
||||
test({.type = _Flags::_Type::__char}, 1, CSTR("c}"));
|
||||
test({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__decimal},
|
||||
1, CSTR("d}"));
|
||||
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}"));
|
||||
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({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__octal},
|
||||
1, 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({.alignment = _Flags::_Alignment::__right,
|
||||
.type = _Flags::_Type::__hexadecimal_lower_case},
|
||||
1, 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>();
|
||||
test<wchar_t>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
#ifndef _WIN32
|
||||
// TODO FMT Investigate why this doesn't work.
|
||||
// (Wait until LWG-3576 has been resolved.)
|
||||
// Make sure the parsers match the expectations. The layout of the
|
||||
// subobjects is chosen to minimize the size required.
|
||||
static_assert(sizeof(Parser<char>) == 2 * sizeof(uint32_t));
|
||||
static_assert(
|
||||
sizeof(Parser<wchar_t>) ==
|
||||
(sizeof(wchar_t) <= 2 ? 2 * sizeof(uint32_t) : 3 * sizeof(uint32_t)));
|
||||
#endif
|
||||
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -587,6 +587,155 @@ void format_test_unsigned_integer(TestFunction check,
|
||||
// TODO FMT Add __uint128_t test after implementing full range.
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_char(TestFunction check, ExceptionTest check_exception) {
|
||||
|
||||
// ***** Char type *****
|
||||
// *** align-fill & width ***
|
||||
check(STR("answer is '* '"), STR("answer is '{:6}'"), CharT('*'));
|
||||
check(STR("answer is ' *'"), STR("answer is '{:>6}'"), CharT('*'));
|
||||
check(STR("answer is '* '"), STR("answer is '{:<6}'"), CharT('*'));
|
||||
check(STR("answer is ' * '"), STR("answer is '{:^6}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is '* '"), STR("answer is '{:6c}'"), CharT('*'));
|
||||
check(STR("answer is ' *'"), STR("answer is '{:>6c}'"), CharT('*'));
|
||||
check(STR("answer is '* '"), STR("answer is '{:<6c}'"), CharT('*'));
|
||||
check(STR("answer is ' * '"), STR("answer is '{:^6c}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is '-----*'"), STR("answer is '{:->6}'"), CharT('*'));
|
||||
check(STR("answer is '*-----'"), STR("answer is '{:-<6}'"), CharT('*'));
|
||||
check(STR("answer is '--*---'"), STR("answer is '{:-^6}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is '-----*'"), STR("answer is '{:->6c}'"), CharT('*'));
|
||||
check(STR("answer is '*-----'"), STR("answer is '{:-<6c}'"), CharT('*'));
|
||||
check(STR("answer is '--*---'"), STR("answer is '{:-^6c}'"), CharT('*'));
|
||||
|
||||
// *** Sign ***
|
||||
check_exception("A sign field isn't allowed in this format-spec", STR("{:-}"),
|
||||
CharT('*'));
|
||||
check_exception("A sign field isn't allowed in this format-spec", STR("{:+}"),
|
||||
CharT('*'));
|
||||
check_exception("A sign field isn't allowed in this format-spec", STR("{: }"),
|
||||
CharT('*'));
|
||||
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("{:-c}"), CharT('*'));
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("{:+c}"), CharT('*'));
|
||||
check_exception("A sign field isn't allowed in this format-spec",
|
||||
STR("{: c}"), CharT('*'));
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("An alternate form field isn't allowed in this format-spec",
|
||||
STR("{:#}"), CharT('*'));
|
||||
check_exception("An alternate form field isn't allowed in this format-spec",
|
||||
STR("{:#c}"), CharT('*'));
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("A zero-padding field isn't allowed in this format-spec",
|
||||
STR("{:0}"), CharT('*'));
|
||||
check_exception("A zero-padding field isn't allowed in this format-spec",
|
||||
STR("{:0c}"), CharT('*'));
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.0}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.42}"), CharT('*'));
|
||||
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.c}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.0c}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.42c}"), CharT('*'));
|
||||
|
||||
// *** locale-specific form ***
|
||||
// Note it has no effect but it's allowed.
|
||||
check(STR("answer is '*'"), STR("answer is '{:L}'"), '*');
|
||||
check(STR("answer is '*'"), STR("answer is '{:Lc}'"), '*');
|
||||
|
||||
// *** type ***
|
||||
for (const auto& fmt : invalid_types<CharT>("bBcdoxX"))
|
||||
check_exception(
|
||||
"The format-spec type has a type not supported for a char argument",
|
||||
fmt, CharT('*'));
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_test_char_as_integer(TestFunction check,
|
||||
ExceptionTest check_exception) {
|
||||
// *** align-fill & width ***
|
||||
check(STR("answer is '42'"), STR("answer is '{:<1d}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is '42'"), STR("answer is '{:<2d}'"), CharT('*'));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<3d}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:7d}'"), CharT('*'));
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:>7d}'"), CharT('*'));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<7d}'"), CharT('*'));
|
||||
check(STR("answer is ' 42 '"), STR("answer is '{:^7d}'"), CharT('*'));
|
||||
|
||||
check(STR("answer is '*****42'"), STR("answer is '{:*>7d}'"), CharT('*'));
|
||||
check(STR("answer is '42*****'"), STR("answer is '{:*<7d}'"), CharT('*'));
|
||||
check(STR("answer is '**42***'"), STR("answer is '{:*^7d}'"), CharT('*'));
|
||||
|
||||
// Test whether zero padding is ignored
|
||||
check(STR("answer is ' 42'"), STR("answer is '{:>07d}'"), CharT('*'));
|
||||
check(STR("answer is '42 '"), STR("answer is '{:<07d}'"), CharT('*'));
|
||||
check(STR("answer is ' 42 '"), STR("answer is '{:^07d}'"), CharT('*'));
|
||||
|
||||
// *** Sign ***
|
||||
check(STR("answer is 42"), STR("answer is {:d}"), CharT('*'));
|
||||
check(STR("answer is 42"), STR("answer is {:-d}"), CharT('*'));
|
||||
check(STR("answer is +42"), STR("answer is {:+d}"), CharT('*'));
|
||||
check(STR("answer is 42"), STR("answer is {: d}"), CharT('*'));
|
||||
|
||||
// *** alternate form ***
|
||||
check(STR("answer is +42"), STR("answer is {:+#d}"), CharT('*'));
|
||||
check(STR("answer is +101010"), STR("answer is {:+b}"), CharT('*'));
|
||||
check(STR("answer is +0b101010"), STR("answer is {:+#b}"), CharT('*'));
|
||||
check(STR("answer is +0B101010"), STR("answer is {:+#B}"), CharT('*'));
|
||||
check(STR("answer is +52"), STR("answer is {:+o}"), CharT('*'));
|
||||
check(STR("answer is +052"), STR("answer is {:+#o}"), CharT('*'));
|
||||
check(STR("answer is +2a"), STR("answer is {:+x}"), CharT('*'));
|
||||
check(STR("answer is +0x2a"), STR("answer is {:+#x}"), CharT('*'));
|
||||
check(STR("answer is +2A"), STR("answer is {:+X}"), CharT('*'));
|
||||
check(STR("answer is +0X2A"), STR("answer is {:+#X}"), CharT('*'));
|
||||
|
||||
// *** zero-padding & width ***
|
||||
check(STR("answer is +00000000042"), STR("answer is {:+#012d}"), CharT('*'));
|
||||
check(STR("answer is +00000101010"), STR("answer is {:+012b}"), CharT('*'));
|
||||
check(STR("answer is +0b000101010"), STR("answer is {:+#012b}"), CharT('*'));
|
||||
check(STR("answer is +0B000101010"), STR("answer is {:+#012B}"), CharT('*'));
|
||||
check(STR("answer is +00000000052"), STR("answer is {:+012o}"), CharT('*'));
|
||||
check(STR("answer is +00000000052"), STR("answer is {:+#012o}"), CharT('*'));
|
||||
check(STR("answer is +0000000002a"), STR("answer is {:+012x}"), CharT('*'));
|
||||
check(STR("answer is +0x00000002a"), STR("answer is {:+#012x}"), CharT('*'));
|
||||
check(STR("answer is +0000000002A"), STR("answer is {:+012X}"), CharT('*'));
|
||||
|
||||
check(STR("answer is +0X00000002A"), STR("answer is {:+#012X}"), CharT('*'));
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.d}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.0d}"), CharT('*'));
|
||||
check_exception("The format-spec should consume the input or end with a '}'",
|
||||
STR("{:.42d}"), CharT('*'));
|
||||
|
||||
// *** 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 a char argument",
|
||||
fmt, '*');
|
||||
}
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
// *** Test escaping ***
|
||||
@@ -622,6 +771,9 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
check(STR("hello 09azAZ!"), STR("hello {}{}{}{}{}{}{}"), CharT('0'),
|
||||
CharT('9'), CharT('a'), CharT('z'), CharT('A'), CharT('Z'), CharT('!'));
|
||||
|
||||
format_test_char<CharT>(check, check_exception);
|
||||
format_test_char_as_integer<CharT>(check, check_exception);
|
||||
|
||||
// *** Test string format argument ***
|
||||
{
|
||||
CharT buffer[] = {CharT('0'), CharT('9'), CharT('a'), CharT('z'),
|
||||
|
||||
Reference in New Issue
Block a user