[libcxx-commits] [libcxx] ade6d0d - [libc++][format] Adds formatter handle.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jan 24 09:15:13 PST 2022


Author: Mark de Wever
Date: 2022-01-24T18:15:08+01:00
New Revision: ade6d0d8fa1d9e327e9a1975351aa6b4b5dbf800

URL: https://github.com/llvm/llvm-project/commit/ade6d0d8fa1d9e327e9a1975351aa6b4b5dbf800
DIFF: https://github.com/llvm/llvm-project/commit/ade6d0d8fa1d9e327e9a1975351aa6b4b5dbf800.diff

LOG: [libc++][format] Adds formatter handle.

This implements the handler according to P0645. P2418 changes the wording
in the Standard. That isn't implemented and requires changes in more
places. LWG3631 applies modifications to P2418, but is currently
unresolved.

Implements parts of:
* P0645 Text Formatting

Depends on D115989

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D115991

Added: 
    libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.handle.pass.cpp

Modified: 
    libcxx/include/__format/format_arg.h
    libcxx/include/format
    libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
    libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
    libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
    libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp
    libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp
    libcxx/test/std/utilities/format/format.functions/format_tests.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h
index 2153287a8a61c..da829d52fbfe4 100644
--- a/libcxx/include/__format/format_arg.h
+++ b/libcxx/include/__format/format_arg.h
@@ -14,7 +14,9 @@
 #include <__config>
 #include <__format/format_error.h>
 #include <__format/format_fwd.h>
+#include <__format/format_parse_context.h>
 #include <__functional_base>
+#include <__memory/addressof.h>
 #include <__variant/monostate.h>
 #include <string>
 #include <string_view>
@@ -56,7 +58,8 @@ enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t {
   __long_double,
   __const_char_type_ptr,
   __string_view,
-  __ptr
+  __ptr,
+  __handle
 };
 } // namespace __format
 
@@ -104,6 +107,8 @@ visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
     return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__string_view);
   case __format::__arg_t::__ptr:
     return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__ptr);
+  case __format::__arg_t::__handle:
+    return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__handle);
   }
   _LIBCPP_UNREACHABLE();
 }
@@ -111,8 +116,7 @@ visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
 template <class _Context>
 class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
 public:
-  // TODO FMT Define the handle class.
-  class handle;
+  class _LIBCPP_TEMPLATE_VIS handle;
 
   _LIBCPP_HIDE_FROM_ABI basic_format_arg() noexcept
       : __type_{__format::__arg_t::__none} {}
@@ -161,7 +165,7 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
     const char_type* __const_char_type_ptr;
     basic_string_view<char_type> __string_view;
     const void* __ptr;
-    // TODO FMT Add the handle.
+    handle __handle;
   };
   __format::__arg_t __type_;
 
@@ -248,6 +252,34 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg {
   template <class _Tp>
   requires is_void_v<_Tp> _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp* __p) noexcept
       : __ptr(__p), __type_(__format::__arg_t::__ptr) {}
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const _Tp& __v) noexcept
+      : __handle(__v), __type_(__format::__arg_t::__handle) {}
+};
+
+template <class _Context>
+class _LIBCPP_TEMPLATE_VIS basic_format_arg<_Context>::handle {
+  friend class basic_format_arg<_Context>;
+
+public:
+  _LIBCPP_HIDE_FROM_ABI
+  void format(basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx) const {
+    __format_(__parse_ctx, __ctx, __ptr_);
+  }
+
+private:
+  const void* __ptr_;
+  void (*__format_)(basic_format_parse_context<char_type>&, _Context&, const void*);
+
+  template <class _Tp>
+  _LIBCPP_HIDE_FROM_ABI explicit handle(const _Tp& __v) noexcept
+      : __ptr_(_VSTD::addressof(__v)),
+        __format_([](basic_format_parse_context<char_type>& __parse_ctx, _Context& __ctx, const void* __ptr) {
+          typename _Context::template formatter_type<_Tp> __f;
+          __parse_ctx.advance_to(__f.parse(__parse_ctx));
+          __ctx.advance_to(__f.format(*static_cast<const _Tp*>(__ptr), __ctx));
+        }) {}
 };
 
 #endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)

diff  --git a/libcxx/include/format b/libcxx/include/format
index 53de4a0e6ca06..c1f1be7d31b98 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -369,6 +369,8 @@ __handle_replacement_field(const _CharT* __begin, const _CharT* __end,
       [&](auto __arg) {
         if constexpr (same_as<decltype(__arg), monostate>)
           __throw_format_error("Argument index out of bounds");
+        else if constexpr (same_as<decltype(__arg), typename basic_format_arg<_Ctx>::handle>)
+          __arg.format(__parse_ctx, __ctx);
         else {
           formatter<decltype(__arg), _CharT> __formatter;
           __parse_ctx.advance_to(__formatter.parse(__parse_ctx));

diff  --git a/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
index 76548c97d77ba..7a534d7282ef7 100644
--- a/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // This test requires the dylib support introduced in D92214.
 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}

diff  --git a/libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp b/libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp
index 6f8035bff7707..da02e6961012c 100644
--- a/libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp
+++ b/libcxx/test/libcxx/utilities/format/format.arguments/format.args/get.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // This test requires the dylib support introduced in D92214.
 // XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}}

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
index 5472944af6684..b2f0dae3edf83 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
index f3539b3c4ad71..72f1d6b4787b8 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
 // UNSUPPORTED: libcpp-has-no-wide-characters
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // Validate it works regardless of the signedness of `char`.
 // RUN: %{cxx} %{flags} %{compile_flags} -fsigned-char -fsyntax-only %s

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
index 50f6885ba8c67..31c66917a5ff5 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
index 2a73d951cc952..0232df433ab06 100644
--- a/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
index e345878b478a1..aae23cb108794 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
index 2a4b3faee9685..41eb8d3797f80 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-localization
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // REQUIRES: locale.en_US.UTF-8
 // REQUIRES: locale.fr_FR.UTF-8

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
index 89f3a36d011f7..fe2a1938000a5 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp
@@ -9,6 +9,8 @@
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-localization
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // REQUIRES: locale.en_US.UTF-8
 // REQUIRES: locale.fr_FR.UTF-8

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp
index 65bca30220d26..3ca3297a151bd 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.char.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.handle.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.handle.pass.cpp
new file mode 100644
index 0000000000000..6d28b4f423fae
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.handle.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+// 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>
+
+// A user defined formatter using
+// template<class Context>
+// class basic_format_arg<Context>::handle
+
+#include <format>
+
+#include <array>
+#include <cassert>
+#include <cmath>
+#include <charconv>
+#include <concepts>
+#include <string>
+#include <type_traits>
+
+#include "test_macros.h"
+
+enum class color { black, red, gold };
+const char* color_names[] = {"black", "red", "gold"};
+
+template <>
+struct std::formatter<color> : std::formatter<const char*> {
+  auto format(color c, format_context& ctx) {
+    return formatter<const char*>::format(color_names[static_cast<int>(c)], ctx);
+  }
+};
+
+void test(std::string expected, std::string_view fmt, color arg) {
+  auto parse_ctx = std::format_parse_context(fmt);
+  std::formatter<color, char> formatter;
+  static_assert(std::semiregular<decltype(formatter)>);
+
+  auto it = formatter.parse(parse_ctx);
+  assert(it == fmt.end() - (!fmt.empty() && fmt.back() == '}'));
+
+  std::string result;
+  auto out = std::back_inserter(result);
+  using FormatCtxT = std::basic_format_context<decltype(out), char>;
+
+  auto format_ctx = std::__format_context_create<decltype(out), char>(out, std::make_format_args<FormatCtxT>(arg));
+  formatter.format(arg, format_ctx);
+  assert(result == expected);
+}
+
+void test_termination_condition(std::string expected, std::string f, color arg) {
+  // 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::string_view fmt{f};
+  assert(fmt.back() == '}' && "Pre-condition failure");
+
+  test(expected, fmt, arg);
+  fmt.remove_suffix(1);
+  test(expected, fmt, arg);
+}
+
+int main(int, char**) {
+  test_termination_condition("black", "}", color::black);
+  test_termination_condition("red", "}", color::red);
+  test_termination_condition("gold", "}", color::gold);
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp
index 8f2d5af2dab91..d25441799483e 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.signed_integral.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp
index 22e330c9daa81..4ee1cfc9dc808 100644
--- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.formatter.spec/formatter.unsigned_integral.pass.cpp
@@ -8,6 +8,8 @@
 // UNSUPPORTED: c++03, c++11, c++14, c++17
 // UNSUPPORTED: libcpp-no-concepts
 // UNSUPPORTED: libcpp-has-no-incomplete-format
+// TODO FMT Evaluate gcc-11 status
+// UNSUPPORTED: gcc-11
 
 // <format>
 

diff  --git a/libcxx/test/std/utilities/format/format.functions/format_tests.h b/libcxx/test/std/utilities/format/format.functions/format_tests.h
index 4b269bbd4e64c..8deed4da43635 100644
--- a/libcxx/test/std/utilities/format/format.functions/format_tests.h
+++ b/libcxx/test/std/utilities/format/format.functions/format_tests.h
@@ -10,11 +10,14 @@
 
 #include <format>
 
+#include <algorithm>
+#include <charconv>
+#include <cmath>
+#include <cstdint>
+
 #include "make_string.h"
 #include "test_macros.h"
 
-#include <cmath>
-
 // In this file the following template types are used:
 // TestFunction must be callable as check(expected-result, string-to-format, args-to-format...)
 // ExceptionTest must be callable as check_exception(expected-exception, string-to-format, args-to-format...)
@@ -40,6 +43,93 @@ struct context<wchar_t> {
 template <class T>
 using context_t = typename context<T>::type;
 
+// A user-defined type used to test the handle formatter.
+enum class status : uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 };
+
+// The formatter for a user-defined type used to test the handle formatter.
+template <class CharT>
+struct std::formatter<status, CharT> {
+  int type = 0;
+
+  constexpr auto parse(auto& parse_ctx) -> decltype(parse_ctx.begin()) {
+    auto begin = parse_ctx.begin();
+    auto end = parse_ctx.end();
+    if (begin == end)
+      return begin;
+
+    switch (*begin) {
+    case CharT('x'):
+      break;
+    case CharT('X'):
+      type = 1;
+      break;
+    case CharT('s'):
+      type = 2;
+      break;
+    case CharT('}'):
+      return begin;
+    default:
+      throw_format_error("The format-spec type has a type not supported for a status argument");
+    }
+
+    ++begin;
+    if (begin != end && *begin != CharT('}'))
+      throw_format_error("The format-spec should consume the input or end with a '}'");
+
+    return begin;
+  }
+
+  auto format(status s, auto& ctx) -> decltype(ctx.out()) {
+    const char* names[] = {"foo", "bar", "foobar"};
+    char buffer[6];
+    const char* begin;
+    const char* end;
+    switch (type) {
+    case 0:
+      begin = buffer;
+      buffer[0] = '0';
+      buffer[1] = 'x';
+      end = std::to_chars(&buffer[2], std::end(buffer), static_cast<uint16_t>(s), 16).ptr;
+      break;
+
+    case 1:
+      begin = buffer;
+      buffer[0] = '0';
+      buffer[1] = 'X';
+      end = std::to_chars(&buffer[2], std::end(buffer), static_cast<uint16_t>(s), 16).ptr;
+      std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) { return std::toupper(c); });
+      break;
+
+    case 2:
+      switch (s) {
+      case status::foo:
+        begin = names[0];
+        break;
+      case status::bar:
+        begin = names[1];
+        break;
+      case status::foobar:
+        begin = names[2];
+        break;
+      }
+      end = begin + strlen(begin);
+      break;
+    }
+
+    return std::copy(begin, end, ctx.out());
+  }
+
+private:
+  void throw_format_error(const char* s) {
+#ifndef _LIBCPP_NO_EXCEPTIONS
+    throw std::format_error(s);
+#else
+    (void)s;
+    std::abort();
+#endif
+  }
+};
+
 template <class CharT>
 std::vector<std::basic_string<CharT>> invalid_types(std::string valid) {
   std::vector<std::basic_string<CharT>> result;
@@ -2518,6 +2608,29 @@ void format_test_pointer(TestFunction check, ExceptionTest check_exception) {
     check_exception("The format-spec type has a type not supported for a pointer argument", fmt, P(nullptr));
 }
 
+template <class CharT, class TestFunction, class ExceptionTest>
+void format_test_handle(TestFunction check, ExceptionTest check_exception) {
+  // *** Valid permuatations ***
+  check(STR("answer is '0xaaaa'"), STR("answer is '{}'"), status::foo);
+  check(STR("answer is '0xaaaa'"), STR("answer is '{:x}'"), status::foo);
+  check(STR("answer is '0XAAAA'"), STR("answer is '{:X}'"), status::foo);
+  check(STR("answer is 'foo'"), STR("answer is '{:s}'"), status::foo);
+
+  check(STR("answer is '0x5555'"), STR("answer is '{}'"), status::bar);
+  check(STR("answer is '0x5555'"), STR("answer is '{:x}'"), status::bar);
+  check(STR("answer is '0X5555'"), STR("answer is '{:X}'"), status::bar);
+  check(STR("answer is 'bar'"), STR("answer is '{:s}'"), status::bar);
+
+  check(STR("answer is '0xaa55'"), STR("answer is '{}'"), status::foobar);
+  check(STR("answer is '0xaa55'"), STR("answer is '{:x}'"), status::foobar);
+  check(STR("answer is '0XAA55'"), STR("answer is '{:X}'"), status::foobar);
+  check(STR("answer is 'foobar'"), STR("answer is '{:s}'"), status::foobar);
+
+  // *** type ***
+  for (const auto& fmt : invalid_types<CharT>("xXs"))
+    check_exception("The format-spec type has a type not supported for a status argument", fmt, status::foo);
+}
+
 template <class CharT, class TestFunction, class ExceptionTest>
 void format_test_pointer(TestFunction check, ExceptionTest check_exception) {
   format_test_pointer<std::nullptr_t, CharT>(check, check_exception);
@@ -2658,6 +2771,9 @@ void format_tests(TestFunction check, ExceptionTest check_exception) {
   check(STR("hello 0x42"), STR("hello {}"), reinterpret_cast<void*>(0x42));
   check(STR("hello 0x42"), STR("hello {}"), reinterpret_cast<const void*>(0x42));
   format_test_pointer<CharT>(check, check_exception);
+
+  // *** Test handle formatter argument ***
+  format_test_handle<CharT>(check, check_exception);
 }
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS


        


More information about the libcxx-commits mailing list