[Boost][Proposal] library for enhanced switch syntax. Request for Comments
Hi All, I wrote a library *eswitch* which provides enhanced functionality for* C++ switch*. Please take a look and leave your comments. *=====================* *Motivation:* *=====================* To overcome "*native switch*" limitations: - one parameter per "*native switch*" - the parameter restricted to only *integral *types( int, char, enum ... ). "*eswitch*" supports any number of parameters and almost without restriction on their *type*, as long as the *type *is comparable( i.e. has* operator==* ). *=====================Why it is important?=====================* Here is the most viewed questions on the *stackoverflow*: FEATURE VIEWS Multiple conditions in switch case https://stackoverflow.com/questions/68578/multiple-cases-in-switch-statement 850k Switch for string https://stackoverflow.com/questions/650162/why-the-switch-statement-cannot-b... 693k Case match in range https://stackoverflow.com/questions/9432226/how-do-i-select-a-range-of-value... 133k Over a million views indicate how many people out there are disappointed with limited functionality of "*native switch*". Building parallels with such operators as "*if*"/"*while*"... it is expected by default that "*native switch*" should have the same possibilities: - be able to work with *non-integral* types as well - be able to compose complex conditions like we do in other operators " *if*"/"*while*"... Unfortunately it is not the case up till now. There was a proposal in 2013 *Relax switch statement http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3627.html*, and committee agreed about importance of this topic. There also was *StringSwitch http://llvm.org/doxygen/classllvm_1_1StringSwitch.html* implementation by *LLVM *and *Case ranges https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html* non-standard extension which is supported by *gcc*, *intel C/C++* and *clang*. Nevertheless, we still don't have common and universal solution. People want to overcome current "*native switch*" limitation either by implementing their own solution, non-standard extension or even trying to standardize it. Making proposed *eswitch https://github.com/rabdumalikov/eswitch_v4* functionality part of the *Boost-Library *will provide millions of developers useful, universal and agile functionality in order to finally fulfill their needs. *=====================repository:=====================* https://github.com/rabdumalikov/eswitch_v4 *=====================References:=====================/// "switch for non-integral" on stackoverflow* 1. https://stackoverflow.com/questions/650162/why-the-switch-statement-cannot-b... 2. https://stackoverflow.com/questions/16388510/evaluate-a-string-with-a-switch... 3. https://stackoverflow.com/questions/5452188/switch-case-statement-in-c-with-... 4. https://stackoverflow.com/questions/19273092/character-in-switch-statement-c 5. https://stackoverflow.com/questions/4165131/c-c-switch-for-non-integers 6. https://stackoverflow.com/questions/27536575/switch-case-on-char */// "Multiple conditions" on stackoverflow* 7. https://stackoverflow.com/questions/8644096/multiple-conditions-in-switch-ca... 8. https://stackoverflow.com/questions/68578/multiple-cases-in-switch-statement */// "Ranges in case" on stackoverflow* 9. https://stackoverflow.com/questions/9432226/how-do-i-select-a-range-of-value... */// "Case ranges" - non-standard extension* 10. https://gcc.gnu.org/onlinedocs/gcc/Case-Ranges.html */// LLVM "StringSwitch"* 11. http://llvm.org/doxygen/classllvm_1_1StringSwitch.html */// "Relax switch statement" proposal* 12. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3627.html */// "N3627 Relaxed switch statement" c++ committee status* 13. https://cplusplus.github.io/EWG/ewg-active.html *Best Regards.*
vigosslive2@gmail.com wrote:
I wrote a library *eswitch* which provides enhanced functionality for* C++ switch*. Please take a look and leave your comments.
I've had a look at your docs, and I don't immediately find anything that explains how it works. Specifically thinking about strings, there are numerous ways that it could be done. Starting with switch (s) { case "aa" : f(); break; case "ab" : g(); break; case "xy" : h(); break; } that can be transformed into: (1): the obvious sequence of if-else-if: if (s == "aa") f(); else if (s == "ab") g(); else if (s == "xy") h(); (2): binary search using nested switches: switch (s <=> "ab") { case -1: switch (s <=> "aa") { case -1: break; case 0: f(); break; case 1: break; } break; case 0: g(); break; case 1: switch (s <=> "xy") { case -1: break; case 0: h(); break; case 1: break; } break; } (3) character-by-character comparison using nested switches: switch (s[0]) { case 'a': switch (s[1]) { case 'a': switch (s[2]) { case 0: f(); break; } case 'b': switch (s[2]) { case 0: g(); break; } } case 'x': switch (s[1]) { case 'y': switch (s[2]) { case 0: h(); break; } } } (4) converting to a 64-bit integer, for strings of up to 8 characters: switch (cast_to_uint64(s)) { case "aa"TOINT: f(); break; case "ab"TOINT: g(); break; case "xy"TOINT: h(); break; } (5) hashing, and then checking: switch (hash(s)) { case "aa"HASH: if (s=="aa") f(); break; case "ab"HASH: if (s=="ab") g(); break; case "xy"HASH: if (s=="xy") h(); break; } I'm sure there are other possibilities. Regarding syntax, there isn't much wrong with just writing the sequence of if-else-ifs. In particular, when the next person comes along and needs to understand this code, which of the following will they prefer: if (s == "aa") f(); else if (s == "ab") g(); else if (s == "xy") h(); or: eswitch(s) >> case_("aa") >> []{ f(); } >> case_("ab") >> []{ g(); } >> case_("xy") >> []{ h(); }; I can see no benefit to your version, UNLESS it results in a more efficient implementation than the if-else-if version. Regards, Phil.
Am 10.11.19 um 15:16 schrieb Phil Endecott via Boost:
Regarding syntax, there isn't much wrong with just writing the sequence of if-else-ifs. In particular, when the next person comes along and needs to understand this code, which of the following will they prefer:
if (s == "aa") f(); else if (s == "ab") g(); else if (s == "xy") h();
or:
eswitch(s) >> case_("aa") >> []{ f(); } >> case_("ab") >> []{ g(); } >> case_("xy") >> []{ h(); };
I can see no benefit to your version, UNLESS it results in a more efficient implementation than the if-else-if version.
Only benefit I see is DRY: The expression to check is only mentioned once. Think of `s` being `CalcSomeValueBasedOnInput(foo, bar, baz)`. The proposed switch allows to put that into the switch statement and never even introduce a variable in the source. There is benefit in doing so, but I'd rather see this as a language feature. Make a `switch-case-default` equivalent to `if-elseif-else` by means of the language and have compiler warning/errors for double cases (e.g. due to C&P) which you don't have for `if-elseif`. So my suggestion would be to write a standards proposal and develop the library solution around it as a PoC. Alex
On Sun, 10 Nov 2019 at 14:16, Phil Endecott via Boost
Specifically thinking about strings, there are numerous ways that it could be done. Starting with
Even for integers there are multiple ways it could be done. It's up to the compiler to decide what is best.
(3) character-by-character comparison using nested switches:
switch (s[0]) { case 'a': switch (s[1]) { case 'a': switch (s[2]) { case 0: f(); break; } case 'b': switch (s[2]) { case 0: g(); break; } } case 'x': switch (s[1]) { case 'y': switch (s[2]) { case 0: h(); break; } } }
That is probably the "natural" implementation that one would expect.
(5) hashing, and then checking:
switch (hash(s)) { case "aa"HASH: if (s=="aa") f(); break; case "ab"HASH: if (s=="ab") g(); break; case "xy"HASH: if (s=="xy") h(); break; }
That code doesn't really need to check, if your hashes collide the compiler will reject the code.
On 11.11.19 10:27, Mathias Gaunard via Boost wrote:
On Sun, 10 Nov 2019 at 14:16, Phil Endecott via Boost
wrote: switch (hash(s)) { case "aa"HASH: if (s=="aa") f(); break; case "ab"HASH: if (s=="ab") g(); break; case "xy"HASH: if (s=="xy") h(); break; }
That code doesn't really need to check, if your hashes collide the compiler will reject the code.
Here 's' could have a value that does not match any of the labels (but with the same hash as one of the labels), so the code does need to check. -- Rainer Deyke (rainerd@eldwood.com)
Mathias Gaunard
On Sun, 10 Nov 2019 at 14:16, Phil Endecott via Boost
wrote: Specifically thinking about strings, there are numerous ways that it could be done. Starting with
Even for integers there are multiple ways it could be done. It's up to the compiler to decide what is best.
Yes, it's important to remember that anything that converts to a conventional switch statement (for example, by hashing the string) then relies on how the compiler chooses to implement it. There are probably different implementations for sparse vs. dense case values, for example. If the compiler turns a sparse switch statement into an if-else-if chain, you might as well have an if-else-if chain in the first place (in terms of performance).
(5) hashing, and then checking:
switch (hash(s)) { case "aa"HASH: if (s=="aa") f(); break; case "ab"HASH: if (s=="ab") g(); break; case "xy"HASH: if (s=="xy") h(); break; }
That code doesn't really need to check, if your hashes collide the compiler will reject the code.
You need to check if s might not match any of the case labels but might collide with them. Regards, Phil.
Phil Endecott wrote:
If the compiler turns a sparse switch statement into an if-else-if chain, you might as well have an if-else-if chain in the first place (in terms of performance).
Compilers also turn an if-else chain into a switch: https://godbolt.org/z/7fgSXH In fact, I think that LLVM first turns the switch into an if-else chain, and then back into a switch in a later pass.
participants (7)
-
Alexander Grund
-
Mathias Gaunard
-
Peter Dimov
-
Phil Endecott
-
Rainer Deyke
-
Seth
-
рустам абдумаликов