xref: /llvm-project/libcxx/test/support/format.functions.common.h (revision bd5d0fee9bbb3762ff26538f03d59926f5635c78)
1 //===----------------------------------------------------------------------===//
2 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3 // See https://llvm.org/LICENSE.txt for license information.
4 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5 //
6 //===----------------------------------------------------------------------===//
7 
8 #ifndef TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
9 #define TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
10 
11 // Contains the common part of the formatter tests for different papers.
12 
13 #include <algorithm>
14 #include <cctype>
15 #include <charconv>
16 #include <format>
17 #include <ranges>
18 #include <string>
19 #include <string_view>
20 #include <vector>
21 
22 #include "make_string.h"
23 
24 #define STR(S) MAKE_STRING(CharT, S)
25 #define SV(S) MAKE_STRING_VIEW(CharT, S)
26 #define CSTR(S) MAKE_CSTRING(CharT, S)
27 
28 template <class T>
29 struct context {};
30 
31 template <>
32 struct context<char> {
33   using type = std::format_context;
34 };
35 
36 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
37 template <>
38 struct context<wchar_t> {
39   using type = std::wformat_context;
40 };
41 #endif
42 
43 template <class T>
44 using context_t = typename context<T>::type;
45 
46 // A user-defined type used to test the handle formatter.
47 enum class status : std::uint16_t { foo = 0xAAAA, bar = 0x5555, foobar = 0xAA55 };
48 
49 // The formatter for a user-defined type used to test the handle formatter.
50 template <class CharT>
51 struct std::formatter<status, CharT> {
52   int type = 0;
53 
54   constexpr auto parse(basic_format_parse_context<CharT>& parse_ctx) -> decltype(parse_ctx.begin()) {
55     auto begin = parse_ctx.begin();
56     auto end = parse_ctx.end();
57     if (begin == end)
58       return begin;
59 
60     switch (*begin) {
61     case CharT('x'):
62       break;
63     case CharT('X'):
64       type = 1;
65       break;
66     case CharT('s'):
67       type = 2;
68       break;
69     case CharT('}'):
70       return begin;
71     default:
72       throw_format_error("The format-spec type has a type not supported for a status argument");
73     }
74 
75     ++begin;
76     if (begin != end && *begin != CharT('}'))
77       throw_format_error("The format-spec should consume the input or end with a '}'");
78 
79     return begin;
80   }
81 
82   template <class Out>
83   auto format(status s, basic_format_context<Out, CharT>& ctx) const -> decltype(ctx.out()) {
84     const char* names[] = {"foo", "bar", "foobar"};
85     char buffer[7];
86     const char* begin = names[0];
87     const char* end = names[0];
88     switch (type) {
89     case 0:
90       begin = buffer;
91       buffer[0] = '0';
92       buffer[1] = 'x';
93       end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr;
94       buffer[6] = '\0';
95       break;
96 
97     case 1:
98       begin = buffer;
99       buffer[0] = '0';
100       buffer[1] = 'X';
101       end = std::to_chars(&buffer[2], std::end(buffer), static_cast<std::uint16_t>(s), 16).ptr;
102       std::transform(static_cast<const char*>(&buffer[2]), end, &buffer[2], [](char c) {
103         return static_cast<char>(std::toupper(c)); });
104       buffer[6] = '\0';
105       break;
106 
107     case 2:
108       switch (s) {
109       case status::foo:
110         begin = names[0];
111         break;
112       case status::bar:
113         begin = names[1];
114         break;
115       case status::foobar:
116         begin = names[2];
117         break;
118       }
119       end = begin + strlen(begin);
120       break;
121     }
122 
123     return std::copy(begin, end, ctx.out());
124   }
125 
126 private:
127   void throw_format_error(const char* s) {
128 #ifndef TEST_HAS_NO_EXCEPTIONS
129     throw std::format_error(s);
130 #else
131     (void)s;
132     std::abort();
133 #endif
134   }
135 };
136 
137 // Creates format string for the invalid types.
138 //
139 // valid contains a list of types that are valid.
140 // - The type ?s is the only type requiring 2 characters, use S for that type.
141 // - Whether n is a type or not depends on the context, is is always used.
142 //
143 // The return value is a collection of basic_strings, instead of
144 // basic_string_views since the values are temporaries.
145 namespace detail {
146 template <class CharT, size_t N>
147 std::basic_string<CharT> get_colons() {
148   static std::basic_string<CharT> result(N, CharT(':'));
149   return result;
150 }
151 
152 constexpr std::string_view get_format_types() {
153   return "aAbBcdeEfFgGopsxX"
154 #if TEST_STD_VER > 20
155          "?"
156 #endif
157       ;
158 }
159 
160 template <class CharT, /*format_types types,*/ size_t N>
161 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
162   // std::ranges::to is not available in C++20.
163   std::vector<std::basic_string<CharT>> result;
164   std::ranges::copy(
165       get_format_types() | std::views::filter([&](char type) { return valid.find(type) == std::string_view::npos; }) |
166           std::views::transform([&](char type) { return std::format(SV("{{{}{}}}"), get_colons<CharT, N>(), type); }),
167       std::back_inserter(result));
168   return result;
169 }
170 
171 } // namespace detail
172 
173 // Creates format string for the invalid types.
174 //
175 // valid contains a list of types that are valid.
176 //
177 // The return value is a collection of basic_strings, instead of
178 // basic_string_views since the values are temporaries.
179 template <class CharT>
180 std::vector<std::basic_string<CharT>> fmt_invalid_types(std::string_view valid) {
181   return detail::fmt_invalid_types<CharT, 1>(valid);
182 }
183 
184 // Like fmt_invalid_types but when the format spec is for an underlying formatter.
185 template <class CharT>
186 std::vector<std::basic_string<CharT>> fmt_invalid_nested_types(std::string_view valid) {
187   return detail::fmt_invalid_types<CharT, 2>(valid);
188 }
189 
190 #endif // TEST_SUPPORT_FORMAT_FUNCTIONS_COMMON_H
191