diff --git a/libcxx/include/__format/formatter_string.h b/libcxx/include/__format/formatter_string.h --- a/libcxx/include/__format/formatter_string.h +++ b/libcxx/include/__format/formatter_string.h @@ -101,6 +101,18 @@ } }; +// Formatter char[]. +template <__formatter::__char_type _CharT, size_t _Size> +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_CharT[_Size], _CharT> + : public __format_spec::__formatter_string<_CharT> { + static_assert(!is_const_v<_CharT>); + using _Base = __format_spec::__formatter_string<_CharT>; + + _LIBCPP_HIDE_FROM_ABI auto format(_CharT __str[_Size], auto& __ctx) -> decltype(__ctx.out()) { + return _Base::format(basic_string_view<_CharT>(__str, _Size), __ctx); + } +}; + // Formatter const char[]. template <__formatter::__char_type _CharT, size_t _Size> struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT diff --git a/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp --- a/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp +++ b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp @@ -82,6 +82,7 @@ assert_is_formattable(); assert_is_formattable(); + assert_is_formattable(); assert_is_formattable, CharT>(); assert_is_formattable, CharT>(); diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char_array.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.char_array.pass.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 +// UNSUPPORTED: apple-clang-12 + +// + +// [format.functions]/25 +// Preconditions: formatter, charT> meets the +// BasicFormatter requirements ([formatter.requirements]) for each Ti in +// Args. + +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +#define STR(S) MAKE_STRING(CharT, S) +#define CSTR(S) MAKE_CSTRING(CharT, S) + +// This is based on the method found in +// clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-cxx20.cpp +template +struct Tester { + // This is not part of the real test, but is used the deduce the size of the input. + constexpr Tester(const char (&r)[N]) { __builtin_memcpy(text, r, N); } + char text[N]; + + // The size of the array shouldn't include the NUL character. + static const size_t size = N - 1; + + template + void test(const std::basic_string& expected, const std::basic_string_view& fmt) const { + using Str = CharT[size]; + std::basic_format_parse_context parse_ctx{fmt}; + std::formatter formatter; + static_assert(std::semiregular); + + auto it = formatter.parse(parse_ctx); + assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}')); + + std::basic_string result; + auto out = std::back_inserter(result); + using FormatCtxT = std::basic_format_context; + + std::basic_string buffer{text, text + N}; + // Note not too found of this hack + Str* data = reinterpret_cast(const_cast(buffer.c_str())); + + auto format_ctx = std::__format_context_create(out, std::make_format_args(*data)); + formatter.format(*data, format_ctx); + assert(result == expected); + } + + template + void test_termination_condition(const std::basic_string& expected, const std::basic_string& f) const { + // The format-spec is valid if completely consumed or terminates at a '}'. + // The valid inputs all end with a '}'. The test is executed twice: + // - first with the terminating '}', + // - second consuming the entire input. + std::basic_string_view fmt{f}; + assert(fmt.back() == CharT('}') && "Pre-condition failure"); + + test(expected, fmt); + fmt.remove_suffix(1); + test(expected, fmt); + } +}; + +template +void test_helper_wrapper(std::basic_string expected, std::basic_string fmt) { + t.test_termination_condition(expected, fmt); +} + +template +void test_array() { + test_helper_wrapper<" azAZ09,./<>?">(STR(" azAZ09,./<>?"), STR("}")); + + std::basic_string s(CSTR("abc\0abc"), 7); + test_helper_wrapper<"abc\0abc">(s, STR("}")); + + test_helper_wrapper<"world">(STR("world"), STR("}")); + test_helper_wrapper<"world">(STR("world"), STR("_>}")); + + test_helper_wrapper<"world">(STR(" world"), STR(">8}")); + test_helper_wrapper<"world">(STR("___world"), STR("_>8}")); + test_helper_wrapper<"world">(STR("_world__"), STR("_^8}")); + test_helper_wrapper<"world">(STR("world___"), STR("_<8}")); + + test_helper_wrapper<"world">(STR("world"), STR(".5}")); + test_helper_wrapper<"universe">(STR("unive"), STR(".5}")); + + test_helper_wrapper<"world">(STR("%world%"), STR("%^7.7}")); + test_helper_wrapper<"universe">(STR("univers"), STR("%^7.7}")); +} + +int main(int, char**) { + test_array(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test_array(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/types.compile.pass.cpp @@ -123,6 +123,7 @@ assert_formatter_is_enabled(); assert_formatter_is_enabled(); + assert_formatter_is_enabled(); assert_formatter_is_enabled(); assert_formatter_is_enabled, CharT>(); assert_formatter_is_enabled, CharT>();