xref: /llvm-project/libcxx/test/support/nasty_string.h (revision 937643b8e1017ce6456de0c05b1673bd9ed0800d)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef TEST_SUPPORT_NASTY_STRING_H
10 #define TEST_SUPPORT_NASTY_STRING_H
11 
12 #include <algorithm>
13 #include <cstddef>
14 #include <string>
15 #include <type_traits>
16 
17 #include "make_string.h"
18 #include "test_macros.h"
19 #include "constexpr_char_traits.h" // is_pointer_in_range
20 
21 // This defines a nasty_string similar to nasty_containers. This string's
22 // value_type does operator hijacking, which allows us to ensure that the
23 // library uses the provided `CharTraits` instead of using operations on
24 // the value_type directly.
25 
26 
27 // When using the code during constant evaluation it relies on
28 //   P2647R1 Permitting static constexpr variables in constexpr functions
29 // This is a C++23 feature, which is not supported by all compilers yet.
30 // * GCC >= 13
31 // * Clang >= 16
32 // * MSVC no support yet
33 //
34 // TODO After there is proper compiler support use TEST_STD_VER >= 23 instead
35 // of this macro in the tests.
36 #if TEST_STD_VER < 23 || __cpp_constexpr < 202211L
37 #  define TEST_HAS_NO_NASTY_STRING
38 #endif
39 
40 #ifndef TEST_HAS_NO_NASTY_STRING
41 // Make sure the char-like operations in strings do not depend on the char-like type.
42 struct nasty_char {
43   template <typename T>
44   friend auto operator<=>(T, T) = delete;
45 
46   template <typename T>
47   friend void operator+(T&&) = delete;
48 
49   template <typename T>
50   friend void operator-(T&&) = delete;
51 
52   template <typename T>
53   friend void operator&(T&&) = delete;
54 
55   char c;
56 };
57 
58 static_assert(std::is_trivial<nasty_char>::value, "");
59 static_assert(std::is_standard_layout<nasty_char>::value, "");
60 
61 // These traits are based on the constexpr_traits test class.
62 struct nasty_char_traits {
63   typedef nasty_char char_type;
64   typedef int int_type;
65   typedef std::streamoff off_type;
66   typedef std::streampos pos_type;
67   typedef std::mbstate_t state_type;
68   // The comparison_category is omitted so the class will have weak_ordering
69   // in C++20. This is intentional.
70 
assignnasty_char_traits71   static constexpr void assign(char_type& c1, const char_type& c2) noexcept { c1 = c2; }
72 
eqnasty_char_traits73   static constexpr bool eq(char_type c1, char_type c2) noexcept { return c1.c == c2.c; }
74 
ltnasty_char_traits75   static constexpr bool lt(char_type c1, char_type c2) noexcept { return c1.c < c2.c; }
76 
77   static constexpr int compare(const char_type* s1, const char_type* s2, std::size_t n);
78   static constexpr std::size_t length(const char_type* s);
79   static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& a);
80   static constexpr char_type* move(char_type* s1, const char_type* s2, std::size_t n);
81   static constexpr char_type* copy(char_type* s1, const char_type* s2, std::size_t n);
82   static constexpr char_type* assign(char_type* s, std::size_t n, char_type a);
83 
not_eofnasty_char_traits84   static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? ~eof() : c; }
85 
to_char_typenasty_char_traits86   static constexpr char_type to_char_type(int_type c) noexcept { return char_type(c); }
87 
to_int_typenasty_char_traits88   static constexpr int_type to_int_type(char_type c) noexcept { return int_type(c.c); }
89 
eq_int_typenasty_char_traits90   static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept { return c1 == c2; }
91 
eofnasty_char_traits92   static constexpr int_type eof() noexcept { return int_type(EOF); }
93 };
94 
compare(const nasty_char * s1,const nasty_char * s2,std::size_t n)95 constexpr int nasty_char_traits::compare(const nasty_char* s1, const nasty_char* s2, std::size_t n) {
96   for (; n; --n, ++s1, ++s2) {
97     if (lt(*s1, *s2))
98       return -1;
99     if (lt(*s2, *s1))
100       return 1;
101   }
102   return 0;
103 }
104 
length(const nasty_char * s)105 constexpr std::size_t nasty_char_traits::length(const nasty_char* s) {
106   std::size_t len = 0;
107   for (; !eq(*s, nasty_char(0)); ++s)
108     ++len;
109   return len;
110 }
111 
find(const nasty_char * s,std::size_t n,const nasty_char & a)112 constexpr const nasty_char* nasty_char_traits::find(const nasty_char* s, std::size_t n, const nasty_char& a) {
113   for (; n; --n) {
114     if (eq(*s, a))
115       return s;
116     ++s;
117   }
118   return 0;
119 }
120 
move(nasty_char * s1,const nasty_char * s2,std::size_t n)121 constexpr nasty_char* nasty_char_traits::move(nasty_char* s1, const nasty_char* s2, std::size_t n) {
122   if (s1 == s2)
123     return s1;
124 
125   nasty_char* r = s1;
126   if (is_pointer_in_range(s1, s1 + n, s2)) {
127     for (; n; --n)
128       assign(*s1++, *s2++);
129   } else {
130     s1 += n;
131     s2 += n;
132     for (; n; --n)
133       assign(*--s1, *--s2);
134   }
135   return r;
136 }
137 
copy(nasty_char * s1,const nasty_char * s2,std::size_t n)138 constexpr nasty_char* nasty_char_traits::copy(nasty_char* s1, const nasty_char* s2, std::size_t n) {
139   if (!std::is_constant_evaluated()) // fails in constexpr because we might be comparing unrelated pointers
140     assert(s2 < s1 || s2 >= s1 + n);
141   nasty_char* r = s1;
142   for (; n; --n, ++s1, ++s2)
143     assign(*s1, *s2);
144   return r;
145 }
146 
assign(nasty_char * s,std::size_t n,nasty_char a)147 constexpr nasty_char* nasty_char_traits::assign(nasty_char* s, std::size_t n, nasty_char a) {
148   nasty_char* r = s;
149   for (; n; --n, ++s)
150     assign(*s, a);
151   return r;
152 }
153 
154 using nasty_string = std::basic_string<nasty_char, nasty_char_traits>;
155 
156 template <std::size_t N>
157 struct ToNastyChar {
ToNastyCharToNastyChar158   constexpr ToNastyChar(const char (&r)[N]) {
159     std::transform(r, r + N, std::addressof(text[0]), [](char c) { return nasty_char{c}; });
160   }
161   nasty_char text[N];
162 };
163 
164 template <std::size_t N>
165 ToNastyChar(const char (&)[N]) -> ToNastyChar<N>;
166 
167 template <ToNastyChar t>
to_nasty_char()168 constexpr auto to_nasty_char() {
169   return t;
170 }
171 
172 // A macro like MAKE_CSTRING
173 //
174 // The difference is this macro can convert the nasty_char too.
175 //
176 // The lambda is a template, so the 'if constexpr' false branch is not evaluated for the nasty_char.
177 #  define CONVERT_TO_CSTRING(CHAR, STR)                                                                                \
178     []<class CharT> {                                                                                                  \
179       if constexpr (std::is_same_v<CharT, nasty_char>) {                                                               \
180         static constexpr auto result = to_nasty_char<STR>();                                                           \
181         return result.text;                                                                                            \
182       } else                                                                                                           \
183         return MAKE_CSTRING(CharT, STR);                                                                               \
184     }.template operator()<CHAR>() /* */
185 #else                             // TEST_HAS_NO_NASTY_STRING
186 #  define CONVERT_TO_CSTRING(CharT, STR) MAKE_CSTRING(CharT, STR)
187 #endif                            // TEST_HAS_NO_NASTY_STRING
188 
189 #endif                            // TEST_SUPPORT_NASTY_STRING_H
190