Skip to content

Commit 4c3f29d

Browse files
kuharAnthonyLatsis
authored andcommitted
[ADT] Allow arbitrary number of cases in StringSwitch (llvm#163117)
Prior to this patch, `.Cases` and `.CasesLower` were **not** variadic functions and accepted a fixed number of arguments: up to 10 for the former and up to 5 for the latter. This is close to unfixable with variadic templates because of the following constraints: * There would be a variadic number of cases and the single value would be at the very end. For a parameter pack approach to work, we would want the value to be the head and the cases strings to follow, but rotating a parameter pack is quite awkward and requires heavy template metaprogramming. * Even if we did rotate the parameter pack, the existing function signature forces conversion of cases to `StringLiteral` and the value to `T`. This is hard to achieve with variadic templates because we can't specify that all cases must be of type `StringLiteral`, and doing the conversion later won't work because of the attribute enable_if in `StringLiteral`'s constructor that doesn't work on non-literal character arrays. * Refactoring all the existing code to put the value first is very intrusive and bugprone when the value parameter is also a string literal. Instead, generalize `.Cases` and `.CasesLower` and make the code manageable by taking an initializer list. The added benefit is that this clearly delineates where case strings end and where the value begins, which can be hard to tell when the value parameter is also a string literal. I plan to eventually deprecate the manually-enumerated overloads of `.Cases.` and `.CasesLower`. (cherry picked from commit 8642762)
1 parent 80066b9 commit 4c3f29d

File tree

2 files changed

+45
-20
lines changed

2 files changed

+45
-20
lines changed

llvm/include/llvm/ADT/StringSwitch.h

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/ADT/StringRef.h"
1717
#include <cassert>
1818
#include <cstring>
19+
#include <initializer_list>
1920
#include <optional>
2021

2122
namespace llvm {
@@ -84,55 +85,60 @@ class StringSwitch {
8485
return *this;
8586
}
8687

88+
StringSwitch &Cases(std::initializer_list<StringLiteral> CaseStrings,
89+
T Value) {
90+
return CasesImpl(Value, CaseStrings);
91+
}
92+
8793
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, T Value) {
88-
return CasesImpl(Value, S0, S1);
94+
return CasesImpl(Value, {S0, S1});
8995
}
9096

9197
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
9298
T Value) {
93-
return CasesImpl(Value, S0, S1, S2);
99+
return CasesImpl(Value, {S0, S1, S2});
94100
}
95101

96102
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
97103
StringLiteral S3, T Value) {
98-
return CasesImpl(Value, S0, S1, S2, S3);
104+
return CasesImpl(Value, {S0, S1, S2, S3});
99105
}
100106

101107
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
102108
StringLiteral S3, StringLiteral S4, T Value) {
103-
return CasesImpl(Value, S0, S1, S2, S3, S4);
109+
return CasesImpl(Value, {S0, S1, S2, S3, S4});
104110
}
105111

106112
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
107113
StringLiteral S3, StringLiteral S4, StringLiteral S5,
108114
T Value) {
109-
return CasesImpl(Value, S0, S1, S2, S3, S4, S5);
115+
return CasesImpl(Value, {S0, S1, S2, S3, S4, S5});
110116
}
111117

112118
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
113119
StringLiteral S3, StringLiteral S4, StringLiteral S5,
114120
StringLiteral S6, T Value) {
115-
return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6);
121+
return CasesImpl(Value, {S0, S1, S2, S3, S4, S5, S6});
116122
}
117123

118124
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
119125
StringLiteral S3, StringLiteral S4, StringLiteral S5,
120126
StringLiteral S6, StringLiteral S7, T Value) {
121-
return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7);
127+
return CasesImpl(Value, {S0, S1, S2, S3, S4, S5, S6, S7});
122128
}
123129

124130
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
125131
StringLiteral S3, StringLiteral S4, StringLiteral S5,
126132
StringLiteral S6, StringLiteral S7, StringLiteral S8,
127133
T Value) {
128-
return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8);
134+
return CasesImpl(Value, {S0, S1, S2, S3, S4, S5, S6, S7, S8});
129135
}
130136

131137
StringSwitch &Cases(StringLiteral S0, StringLiteral S1, StringLiteral S2,
132138
StringLiteral S3, StringLiteral S4, StringLiteral S5,
133139
StringLiteral S6, StringLiteral S7, StringLiteral S8,
134140
StringLiteral S9, T Value) {
135-
return CasesImpl(Value, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9);
141+
return CasesImpl(Value, {S0, S1, S2, S3, S4, S5, S6, S7, S8, S9});
136142
}
137143

138144
// Case-insensitive case matchers.
@@ -155,23 +161,28 @@ class StringSwitch {
155161
return *this;
156162
}
157163

164+
StringSwitch &CasesLower(std::initializer_list<StringLiteral> CaseStrings,
165+
T Value) {
166+
return CasesLowerImpl(Value, CaseStrings);
167+
}
168+
158169
StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, T Value) {
159-
return CasesLowerImpl(Value, S0, S1);
170+
return CasesLowerImpl(Value, {S0, S1});
160171
}
161172

162173
StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
163174
T Value) {
164-
return CasesLowerImpl(Value, S0, S1, S2);
175+
return CasesLowerImpl(Value, {S0, S1, S2});
165176
}
166177

167178
StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
168179
StringLiteral S3, T Value) {
169-
return CasesLowerImpl(Value, S0, S1, S2, S3);
180+
return CasesLowerImpl(Value, {S0, S1, S2, S3});
170181
}
171182

172183
StringSwitch &CasesLower(StringLiteral S0, StringLiteral S1, StringLiteral S2,
173184
StringLiteral S3, StringLiteral S4, T Value) {
174-
return CasesLowerImpl(Value, S0, S1, S2, S3, S4);
185+
return CasesLowerImpl(Value, {S0, S1, S2, S3, S4});
175186
}
176187

177188
[[nodiscard]] R Default(T Value) {
@@ -205,16 +216,21 @@ class StringSwitch {
205216
return false;
206217
}
207218

208-
template <typename... Args> StringSwitch &CasesImpl(T &Value, Args... Cases) {
219+
StringSwitch &CasesImpl(T &Value,
220+
std::initializer_list<StringLiteral> Cases) {
209221
// Stop matching after the string is found.
210-
(... || CaseImpl(Value, Cases));
222+
for (StringLiteral S : Cases)
223+
if (CaseImpl(Value, S))
224+
break;
211225
return *this;
212226
}
213227

214-
template <typename... Args>
215-
StringSwitch &CasesLowerImpl(T &Value, Args... Cases) {
228+
StringSwitch &CasesLowerImpl(T &Value,
229+
std::initializer_list<StringLiteral> Cases) {
216230
// Stop matching after the string is found.
217-
(... || CaseLowerImpl(Value, Cases));
231+
for (StringLiteral S : Cases)
232+
if (CaseLowerImpl(Value, S))
233+
break;
218234
return *this;
219235
}
220236
};

llvm/unittests/ADT/StringSwitchTest.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,14 @@ TEST(StringSwitchTest, EndsWithLower) {
153153
}
154154

155155
TEST(StringSwitchTest, Cases) {
156-
enum class OSType { Windows, Linux, Unknown };
156+
enum class OSType { Windows, Linux, MacOS, Unknown };
157157

158158
auto Translate = [](StringRef S) {
159159
return llvm::StringSwitch<OSType>(S)
160160
.Cases(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",
161161
OSType::Windows)
162162
.Cases("linux", "unix", "*nix", "posix", OSType::Linux)
163+
.Cases({"macos", "osx"}, OSType::MacOS)
163164
.Default(OSType::Unknown);
164165
};
165166

@@ -172,21 +173,26 @@ TEST(StringSwitchTest, Cases) {
172173
EXPECT_EQ(OSType::Linux, Translate("*nix"));
173174
EXPECT_EQ(OSType::Linux, Translate("posix"));
174175

176+
EXPECT_EQ(OSType::MacOS, Translate("macos"));
177+
EXPECT_EQ(OSType::MacOS, Translate("osx"));
178+
175179
// Note that the whole null-terminator embedded string is required for the
176180
// case to match.
177181
EXPECT_EQ(OSType::Unknown, Translate("wind"));
178182
EXPECT_EQ(OSType::Unknown, Translate("Windows"));
183+
EXPECT_EQ(OSType::Unknown, Translate("MacOS"));
179184
EXPECT_EQ(OSType::Unknown, Translate(""));
180185
}
181186

182187
TEST(StringSwitchTest, CasesLower) {
183-
enum class OSType { Windows, Linux, Unknown };
188+
enum class OSType { Windows, Linux, MacOS, Unknown };
184189

185190
auto Translate = [](StringRef S) {
186191
return llvm::StringSwitch<OSType>(S)
187192
.CasesLower(StringLiteral::withInnerNUL("wind\0ws"), "win32", "winnt",
188193
OSType::Windows)
189194
.CasesLower("linux", "unix", "*nix", "posix", OSType::Linux)
195+
.CasesLower({"macos", "osx"}, OSType::MacOS)
190196
.Default(OSType::Unknown);
191197
};
192198

@@ -202,6 +208,9 @@ TEST(StringSwitchTest, CasesLower) {
202208
EXPECT_EQ(OSType::Windows, Translate(llvm::StringRef("wind\0ws", 7)));
203209
EXPECT_EQ(OSType::Linux, Translate("linux"));
204210

211+
EXPECT_EQ(OSType::MacOS, Translate("macOS"));
212+
EXPECT_EQ(OSType::MacOS, Translate("OSX"));
213+
205214
EXPECT_EQ(OSType::Unknown, Translate("wind"));
206215
EXPECT_EQ(OSType::Unknown, Translate(""));
207216
}

0 commit comments

Comments
 (0)