1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef TEST_SUPPORT_NASTY_STRING_H
10 #define TEST_SUPPORT_NASTY_STRING_H
15 #include <type_traits>
17 #include "make_string.h"
18 #include "test_macros.h"
19 #include "constexpr_char_traits.h" // is_pointer_in_range
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.
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.
32 // * MSVC no support yet
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
40 #ifndef TEST_HAS_NO_NASTY_STRING
41 // Make sure the char-like operations in strings do not depend on the char-like type.
44 friend auto operator<=>(T
, T
) = delete;
47 friend void operator+(T
&&) = delete;
50 friend void operator-(T
&&) = delete;
53 friend void operator&(T
&&) = delete;
58 static_assert(std::is_trivial
<nasty_char
>::value
, "");
59 static_assert(std::is_standard_layout
<nasty_char
>::value
, "");
61 // These traits are based on the constexpr_traits test class.
62 struct nasty_char_traits
{
63 typedef nasty_char char_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.
71 static constexpr void assign(char_type
& c1
, const char_type
& c2
) noexcept
{ c1
= c2
; }
73 static constexpr bool eq(char_type c1
, char_type c2
) noexcept
{ return c1
.c
== c2
.c
; }
75 static constexpr bool lt(char_type c1
, char_type c2
) noexcept
{ return c1
.c
< c2
.c
; }
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
);
84 static constexpr int_type
not_eof(int_type c
) noexcept
{ return eq_int_type(c
, eof()) ? ~eof() : c
; }
86 static constexpr char_type
to_char_type(int_type c
) noexcept
{ return char_type(c
); }
88 static constexpr int_type
to_int_type(char_type c
) noexcept
{ return int_type(c
.c
); }
90 static constexpr bool eq_int_type(int_type c1
, int_type c2
) noexcept
{ return c1
== c2
; }
92 static constexpr int_type
eof() noexcept
{ return int_type(EOF
); }
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
) {
105 constexpr std::size_t nasty_char_traits::length(const nasty_char
* s
) {
107 for (; !eq(*s
, nasty_char(0)); ++s
)
112 constexpr const nasty_char
* nasty_char_traits::find(const nasty_char
* s
, std::size_t n
, const nasty_char
& a
) {
121 constexpr nasty_char
* nasty_char_traits::move(nasty_char
* s1
, const nasty_char
* s2
, std::size_t n
) {
126 if (is_pointer_in_range(s1
, s1
+ n
, s2
)) {
128 assign(*s1
++, *s2
++);
133 assign(*--s1
, *--s2
);
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
);
142 for (; n
; --n
, ++s1
, ++s2
)
147 constexpr nasty_char
* nasty_char_traits::assign(nasty_char
* s
, std::size_t n
, nasty_char a
) {
154 using nasty_string
= std::basic_string
<nasty_char
, nasty_char_traits
>;
156 template <std::size_t N
>
158 constexpr ToNastyChar(const char (&r
)[N
]) {
159 std::transform(r
, r
+ N
, std::addressof(text
[0]), [](char c
) { return nasty_char
{c
}; });
164 template <std::size_t N
>
165 ToNastyChar(const char (&)[N
]) -> ToNastyChar
<N
>;
167 template <ToNastyChar t
>
168 constexpr auto to_nasty_char() {
172 // A macro like MAKE_CSTRING
174 // The difference is this macro can convert the nasty_char too.
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) \
179 if constexpr (std::is_same_v<CharT, nasty_char>) { \
180 static constexpr auto result = to_nasty_char<STR>(); \
181 return result.text; \
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
189 #endif // TEST_SUPPORT_NASTY_STRING_H