|
1 | 1 | #ifndef QX_STRINGLITERAL_H |
2 | 2 | #define QX_STRINGLITERAL_H |
3 | 3 |
|
| 4 | +// Standard Library Includes |
4 | 5 | #include <algorithm> |
| 6 | +#include <concepts> |
| 7 | + |
| 8 | +// Qt Includes |
| 9 | +#include <QLatin1String> |
| 10 | + |
| 11 | +// Intra-component Includes |
| 12 | +#include "qx/utility/qx-concepts.h" |
| 13 | + |
| 14 | +/* This base class is an implementation detail that makes it much easier (if not outright |
| 15 | + * possible at all) to make concepts that need to work for all StringLiteral types while |
| 16 | + * ignoring the size parameter N. Once Clang 19+ is common (see below todo), this won't |
| 17 | + * be needed. |
| 18 | + */ |
| 19 | +/*! @cond */ |
| 20 | +namespace _QxPrivate |
| 21 | +{ |
| 22 | + |
| 23 | +template<std::integral C> |
| 24 | +class StringLiteralBase {}; |
| 25 | + |
| 26 | +} |
| 27 | +/*! @endcond */ |
5 | 28 |
|
6 | 29 | namespace Qx |
7 | 30 | { |
8 | 31 |
|
9 | | -template<size_t N> |
10 | | -struct StringLiteral |
| 32 | +// These concepts won't be needed either as per-above and todo. |
| 33 | +template<typename T> |
| 34 | +concept string_literal = derived_from_specialization_of<T, _QxPrivate::StringLiteralBase>; |
| 35 | + |
| 36 | +template<typename T, typename U> |
| 37 | +concept compatible_string_literals = |
| 38 | + string_literal<T> && |
| 39 | + string_literal<U> && |
| 40 | + std::same_as<typename T::data_t, typename U::data_t>; |
| 41 | + |
| 42 | +template<typename S, typename C> |
| 43 | +concept compatible_string_literal_operand = |
| 44 | + string_literal<S> && |
| 45 | + std::same_as<typename S::data_t, C>; |
| 46 | + |
| 47 | +template<std::integral C, size_t N> |
| 48 | +class StringLiteral : public _QxPrivate::StringLiteralBase<C> |
11 | 49 | { |
12 | | -//-Instance Fields--------------------------------------------------------------------------------------------------- |
13 | | - char value[N]; |
| 50 | +//-Aliases---------------------------------------------------------------------------------------------------------- |
| 51 | +public: |
| 52 | + using data_t = C; |
| 53 | + |
| 54 | + using view_t = std::conditional_t< |
| 55 | + std::is_same_v<C, char>, |
| 56 | + QLatin1StringView, |
| 57 | + std::conditional_t< |
| 58 | + std::is_same_v<C, char16_t>, |
| 59 | + QStringView, |
| 60 | + void |
| 61 | + > |
| 62 | + >; |
| 63 | + |
| 64 | +/*! @cond */ |
| 65 | + template<size_t M> using rebind = StringLiteral<C, M>; // Impl detail |
| 66 | +/*! @endcond */ |
| 67 | + |
| 68 | +//-Class Variables--------------------------------------------------------------------------------------------------- |
| 69 | +public: |
| 70 | + static constexpr size_t size_v = N - 1; |
| 71 | + |
| 72 | +//-Instance Variables--------------------------------------------------------------------------------------------------- |
| 73 | +public: |
| 74 | +/*! @cond */ |
| 75 | + C _str[N]; // Must be public due to C++20 limitations |
| 76 | +/*! @endcond */ |
14 | 77 |
|
15 | 78 | //-Constructor---------------------------------------------------------------------------------------------------------- |
16 | | - constexpr StringLiteral(const char (&str)[N]) |
17 | | - { |
18 | | - std::copy_n(str, N, value); |
19 | | - } |
| 79 | +public: |
| 80 | + constexpr StringLiteral(const C (&str)[N]) { std::copy_n(str, N, _str); } |
| 81 | + |
| 82 | +//-Instance Functions---------------------------------------------------------------------------------------------------- |
| 83 | +public: |
| 84 | + constexpr C* data() const { return _str; } |
| 85 | + constexpr size_t size() const { return N - 1; } |
| 86 | + constexpr view_t view() const requires (!std::same_as<view_t, void>){ return view_t(*this); } |
| 87 | + constexpr std::basic_string_view<C> std_view() const { return std::basic_string_view<C>(*this); } |
| 88 | + |
| 89 | +//-Operators-------------------------------------------------------------------------------------------------------- |
| 90 | +public: |
| 91 | + constexpr std::strong_ordering operator<=>(const StringLiteral& other) const = default; |
| 92 | + constexpr bool operator==(const StringLiteral& other) const = default; |
| 93 | + constexpr operator QLatin1StringView() const requires std::same_as<C, char> { return QLatin1StringView(_str, N - 1); } |
| 94 | + constexpr operator QStringView() const requires std::same_as<C, char16_t > { return QStringView(_str, N - 1); } |
| 95 | + constexpr operator std::basic_string_view<C>() const { return std::basic_string_view<C>(_str, N - 1); } |
20 | 96 | }; |
21 | 97 |
|
22 | | -template <size_t N> |
23 | | -struct StringLiteral16 |
| 98 | +template<typename StringLiteralA, typename StringLiteralB> |
| 99 | + requires compatible_string_literals<StringLiteralA, StringLiteralB> |
| 100 | +constexpr auto operator+(const StringLiteralA& a, const StringLiteralA& b) |
| 101 | +{ |
| 102 | + // It's important to note here than L1 and L2 are sizes without |
| 103 | + using C = typename StringLiteralA::data_t; |
| 104 | + constexpr size_t L1 = StringLiteralA::size_v; |
| 105 | + constexpr size_t L2 = StringLiteralB::size_v; |
| 106 | + constexpr size_t R = L1 + L2 +1; |
| 107 | + using result_t = typename StringLiteralA::template rebind<R>; |
| 108 | + /* Separate buffer is an extra copy, vs just making the result string and copying |
| 109 | + * into it directly, but this avoids the cruft of having to make these all friends |
| 110 | + * with the class and the speed loss is largely irrelevant since these are used |
| 111 | + * at compile time. |
| 112 | + */ |
| 113 | + |
| 114 | + |
| 115 | + C buff[R] ; |
| 116 | + std::copy_n(a._str, L1, buff); // a chars |
| 117 | + std::copy_n(b._str, L2 + 1, buff + L1); // b chars + '/0' |
| 118 | + return result_t(buff); |
| 119 | +} |
| 120 | + |
| 121 | +template<typename S, std::integral C, size_t N2> |
| 122 | + requires compatible_string_literal_operand<S, C> |
| 123 | +constexpr auto operator+(const S& a, const C (&b)[N2]) |
24 | 124 | { |
25 | | - char16_t value[N]; |
| 125 | + // It's important to note here than N2 is a size including '/0' and L1 is a size without '/0' |
| 126 | + constexpr size_t L1 = S::size_v; |
| 127 | + constexpr size_t R = L1 + N2; |
| 128 | + using result_t = typename S::template rebind<R>; |
26 | 129 |
|
27 | | - constexpr StringLiteral16(const char16_t (&str)[N]) |
28 | | - { |
29 | | - std::copy_n(str, N, value); |
30 | | - } |
| 130 | + /* Separate buffer is an extra copy, vs just making the result string and copying |
| 131 | + * into it directly, but this avoids the cruft of having to make these all friends |
| 132 | + * with the class and the speed loss is largely irrelevant since these are used |
| 133 | + * at compile time. |
| 134 | + */ |
| 135 | + C buff[R] ; // a chars + (b chars + '/0') |
| 136 | + std::copy_n(a._str, L1, buff); // a chars |
| 137 | + std::copy_n(b, N2, buff + L1); // b chars + '/0' |
| 138 | + return result_t(buff); |
| 139 | +} |
| 140 | + |
| 141 | +template<std::integral C, size_t N1, typename S> |
| 142 | + requires compatible_string_literal_operand<S, C> |
| 143 | +constexpr auto operator+(const C (&a)[N1], const S& b) { return b + a; } |
| 144 | + |
| 145 | +/* TOOO: We use derivations here instead of template aliases as parameter deduction for aliases |
| 146 | + * (what allows the parameter to deduce the character type and size just based on the |
| 147 | + * type of string), P1814R0, as not implemented in Clang until 19! |
| 148 | + * |
| 149 | + * Once that version isn't that new, we can switch back to using aliases which should make the |
| 150 | + * operator+() definition easier, as it will be able to look like (for example): |
| 151 | + * |
| 152 | + * template<std::integral C, size_t N1, size_t N2> |
| 153 | + * constexpr auto operator+(const StringLiteral<C, N1>& a, const StringLiteral<C, N2>& b) |
| 154 | + * { |
| 155 | + * * Separate buffer is an extra copy, vs just making the result string and copying |
| 156 | + * * into it directly, but this avoids the cruft of having to make these all friends |
| 157 | + * * with the class and the speed loss is largely irrelevant since these are used |
| 158 | + * * at compile time. |
| 159 | + * * |
| 160 | + * C buff[N1 + N2 - 1] ; |
| 161 | + * std::copy_n(a._str, N1 - 1, buff); // a chars |
| 162 | + * std::copy_n(b._str, N2, buff + (N1 - 1)); // b chars + '/0' |
| 163 | + * return StringLiteral<C, N1 + N2 - 1>(buff); |
| 164 | + * } |
| 165 | + * |
| 166 | + * instead of having to use the current constraint workaround that needs to allow derived types, |
| 167 | + * which means we can remove a bunch of boilerplate like the 'rebind' alias |
| 168 | + */ |
| 169 | +template<size_t N> |
| 170 | +struct CStringLiteral final : public StringLiteral<char, N> |
| 171 | +{ |
| 172 | + constexpr CStringLiteral(const char (&str)[N]) : StringLiteral<char, N>(str) {} |
| 173 | + /*! @cond */ template<size_t M> using rebind = CStringLiteral<M>; /*! @endcond */ // Impl detail |
| 174 | +}; |
| 175 | + |
| 176 | +template<size_t N> |
| 177 | +struct WStringLiteral final : public StringLiteral<wchar_t, N> |
| 178 | +{ |
| 179 | + constexpr WStringLiteral(const wchar_t (&str)[N]) : StringLiteral<wchar_t, N>(str) {} |
| 180 | + /*! @cond */ template<size_t M> using rebind = WStringLiteral<M>; /*! @endcond */ // Impl detail |
| 181 | +}; |
| 182 | + |
| 183 | +template<size_t N> |
| 184 | +struct U8StringLiteral final : public StringLiteral<char8_t, N> |
| 185 | +{ |
| 186 | + constexpr U8StringLiteral(const char8_t (&str)[N]) : StringLiteral<char8_t, N>(str) {} |
| 187 | + /*! @cond */ template<size_t M> using rebind = U8StringLiteral<M>; /*! @endcond */ // Impl detail |
| 188 | +}; |
| 189 | + |
| 190 | +template<size_t N> |
| 191 | +struct U16StringLiteral final : public StringLiteral<char16_t, N> |
| 192 | +{ |
| 193 | + constexpr U16StringLiteral(const char16_t (&str)[N]) : StringLiteral<char16_t, N>(str) {} |
| 194 | + /*! @cond */ template<size_t M> using rebind = U16StringLiteral<M>; /*! @endcond */ // Impl detail |
| 195 | +}; |
| 196 | + |
| 197 | +template<size_t N> |
| 198 | +struct U32StringLiteral final : public StringLiteral<char32_t, N> |
| 199 | +{ |
| 200 | + constexpr U32StringLiteral(const char32_t (&str)[N]) : StringLiteral<char32_t, N>(str) {} |
| 201 | + /*! @cond */ template<size_t M> using rebind = U32StringLiteral<M>; /*! @endcond */ // Impl detail |
31 | 202 | }; |
32 | 203 |
|
33 | 204 | } |
|
0 commit comments