LLVM 22.0.0git
StringSwitch.h
Go to the documentation of this file.
1//===--- StringSwitch.h - Switch-on-literal-string Construct --------------===/
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/// \file
9/// This file implements the StringSwitch template, which mimics a switch()
10/// statement whose cases are string literals.
11///
12//===----------------------------------------------------------------------===/
13#ifndef LLVM_ADT_STRINGSWITCH_H
14#define LLVM_ADT_STRINGSWITCH_H
15
16#include "llvm/ADT/StringRef.h"
18#include <cassert>
19#include <cstring>
20#include <initializer_list>
21#include <optional>
22
23namespace llvm {
24
25/// A switch()-like statement whose cases are string literals.
26///
27/// The StringSwitch class is a simple form of a switch() statement that
28/// determines whether the given string matches one of the given string
29/// literals. The template type parameter \p T is the type of the value that
30/// will be returned from the string-switch expression. For example,
31/// the following code switches on the name of a color in \c argv[i]:
32///
33/// \code
34/// Color color = StringSwitch<Color>(argv[i])
35/// .Case("red", Red)
36/// .Case("orange", Orange)
37/// .Case("yellow", Yellow)
38/// .Case("green", Green)
39/// .Case("blue", Blue)
40/// .Case("indigo", Indigo)
41/// .Cases({"violet", "purple"}, Violet)
42/// .Default(UnknownColor);
43/// \endcode
44///
45/// When multiple matches are found, the value of the first match is returned.
46template<typename T, typename R = T>
48 /// The string we are matching.
49 const StringRef Str;
50
51 /// The pointer to the result of this switch statement, once known,
52 /// null before that.
53 std::optional<T> Result;
54
55public:
57 : Str(S), Result() { }
58
60
61 // StringSwitch is not copyable.
62 StringSwitch(const StringSwitch &) = delete;
63
64 // StringSwitch is not assignable due to 'Str' being 'const'.
65 void operator=(const StringSwitch &) = delete;
66 void operator=(StringSwitch &&) = delete;
67
68 // Case-sensitive case matchers.
70 CaseImpl(S, Value);
71 return *this;
72 }
73
75 if (!Result && Str.ends_with(S)) {
76 Result = std::move(Value);
77 }
78 return *this;
79 }
80
82 if (!Result && Str.starts_with(S)) {
83 Result = std::move(Value);
84 }
85 return *this;
86 }
87
88 StringSwitch &Cases(std::initializer_list<StringLiteral> CaseStrings,
89 T Value) {
90 return CasesImpl(CaseStrings, Value);
91 }
92
93 [[deprecated("Pass cases in std::initializer_list instead")]]
95 return CasesImpl({S0, S1}, Value);
96 }
97
98 [[deprecated("Pass cases in std::initializer_list instead")]]
100 T Value) {
101 return CasesImpl({S0, S1, S2}, Value);
102 }
103
104 [[deprecated("Pass cases in std::initializer_list instead")]]
106 StringLiteral S3, T Value) {
107 return CasesImpl({S0, S1, S2, S3}, Value);
108 }
109
110 [[deprecated("Pass cases in std::initializer_list instead")]]
113 return CasesImpl({S0, S1, S2, S3, S4}, Value);
114 }
115
116 [[deprecated("Pass cases in std::initializer_list instead")]]
119 T Value) {
120 return CasesImpl({S0, S1, S2, S3, S4, S5}, Value);
121 }
122
123 [[deprecated("Pass cases in std::initializer_list instead")]]
126 StringLiteral S6, T Value) {
127 return CasesImpl({S0, S1, S2, S3, S4, S5, S6}, Value);
128 }
129
130 [[deprecated("Pass cases in std::initializer_list instead")]]
134 return CasesImpl({S0, S1, S2, S3, S4, S5, S6, S7}, Value);
135 }
136
137 [[deprecated("Pass cases in std::initializer_list instead")]]
141 T Value) {
142 return CasesImpl({S0, S1, S2, S3, S4, S5, S6, S7, S8}, Value);
143 }
144
145 [[deprecated("Pass cases in std::initializer_list instead")]]
149 StringLiteral S9, T Value) {
150 return CasesImpl({S0, S1, S2, S3, S4, S5, S6, S7, S8, S9}, Value);
151 }
152
153 // Case-insensitive case matchers.
155 CaseLowerImpl(S, Value);
156 return *this;
157 }
158
160 if (!Result && Str.ends_with_insensitive(S))
161 Result = Value;
162
163 return *this;
164 }
165
167 if (!Result && Str.starts_with_insensitive(S))
168 Result = std::move(Value);
169
170 return *this;
171 }
172
173 StringSwitch &CasesLower(std::initializer_list<StringLiteral> CaseStrings,
174 T Value) {
175 return CasesLowerImpl(CaseStrings, Value);
176 }
177
178 [[deprecated("Pass cases in std::initializer_list instead")]]
180 return CasesLowerImpl({S0, S1}, Value);
181 }
182
183 [[deprecated("Pass cases in std::initializer_list instead")]]
185 T Value) {
186 return CasesLowerImpl({S0, S1, S2}, Value);
187 }
188
189 [[deprecated("Pass cases in std::initializer_list instead")]]
191 StringLiteral S3, T Value) {
192 return CasesLowerImpl({S0, S1, S2, S3}, Value);
193 }
194
195 [[deprecated("Pass cases in std::initializer_list instead")]]
198 return CasesLowerImpl({S0, S1, S2, S3, S4}, Value);
199 }
200
201 [[nodiscard]] R Default(T Value) {
202 if (Result)
203 return std::move(*Result);
204 return Value;
205 }
206
207 /// Declare default as unreachable, making sure that all cases were handled.
208 [[nodiscard]] R DefaultUnreachable(
209 const char *Message = "Fell off the end of a string-switch") {
210 if (Result)
211 return std::move(*Result);
212 llvm_unreachable(Message);
213 }
214
215 [[nodiscard]] operator R() { return DefaultUnreachable(); }
216
217private:
218 // Returns true when a match is found. If `Str` matches the `S` argument,
219 // stores the result.
220 bool CaseImpl(StringLiteral S, T &Value) {
221 if (Result)
222 return true;
223
224 if (Str != S)
225 return false;
226
227 Result = std::move(Value);
228 return true;
229 }
230
231 // Returns true when a match is found. If `Str` matches the `S` argument
232 // (case-insensitive), stores the result.
233 bool CaseLowerImpl(StringLiteral S, T &Value) {
234 if (Result)
235 return true;
236
237 if (!Str.equals_insensitive(S))
238 return false;
239
240 Result = std::move(Value);
241 return true;
242 }
243
244 StringSwitch &CasesImpl(std::initializer_list<StringLiteral> Cases,
245 T &Value) {
246 // Stop matching after the string is found.
247 for (StringLiteral S : Cases)
248 if (CaseImpl(S, Value))
249 break;
250 return *this;
251 }
252
253 StringSwitch &CasesLowerImpl(std::initializer_list<StringLiteral> Cases,
254 T &Value) {
255 // Stop matching after the string is found.
256 for (StringLiteral S : Cases)
257 if (CaseLowerImpl(S, Value))
258 break;
259 return *this;
260 }
261};
262
263} // end namespace llvm
264
265#endif // LLVM_ADT_STRINGSWITCH_H
constexpr LLT S1
constexpr LLT S8
#define T
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
Definition StringRef.h:854
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
StringSwitch & CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, StringLiteral S5, T Value)
StringSwitch & EndsWithLower(StringLiteral S, T Value)
StringSwitch & StartsWithLower(StringLiteral S, T Value)
StringSwitch & CaseLower(StringLiteral S, T Value)
StringSwitch & Case(StringLiteral S, T Value)
StringSwitch & CasesLower(StringLiteral S0, StringLiteral S1, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, T Value)
void operator=(StringSwitch &&)=delete
void operator=(const StringSwitch &)=delete
StringSwitch & CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, T Value)
R DefaultUnreachable(const char *Message="Fell off the end of a string-switch")
Declare default as unreachable, making sure that all cases were handled.
StringSwitch & CasesLower(std::initializer_list< StringLiteral > CaseStrings, T Value)
StringSwitch & StartsWith(StringLiteral S, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, StringLiteral S5, StringLiteral S6, StringLiteral S7, StringLiteral S8, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, T Value)
StringSwitch(const StringSwitch &)=delete
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, T Value)
StringSwitch & EndsWith(StringLiteral S, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, StringLiteral S5, StringLiteral S6, StringLiteral S7, T Value)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, StringLiteral S5, StringLiteral S6, T Value)
StringSwitch(StringSwitch &&)=default
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, T Value)
StringSwitch & Cases(std::initializer_list< StringLiteral > CaseStrings, T Value)
StringSwitch(StringRef S)
StringSwitch & Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2, StringLiteral S3, StringLiteral S4, StringLiteral S5, StringLiteral S6, StringLiteral S7, StringLiteral S8, StringLiteral S9, T Value)
StringSwitch & CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2, T Value)
LLVM Value Representation.
Definition Value.h:75
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
Definition InstrProf.h:137