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