A more advanced switch

234 views
Skip to first unread message

costeln...@gmail.com

unread,
Aug 1, 2017, 5:24:04 PM8/1/17
to ISO C++ Standard - Future Proposals
Hello, I'm sorry if you talked about this here before, but I didn't watch this group and I couldn't find this thing when searching.

I'm coming from another language named Pawn, it looks like C ~80%, but it has more advantages than C. Here's the official page: https://www.compuphase.com/pawn/pawn.htm .

A problem in C++: "switch" is exactly like in C, I think it deserves an upgrade, as it can be used a lot, providing more speed and easier syntax than lots of "if" ... "else if" ... "else" lines. I know that simply updating it would break compatibility, so it may require another name instead of switch, or you can find any other backwards compatible alternative. I bet you can do this perfectly.

For cases 0, 1, 2, 3, 4 and 5, 6 and "default" in C++ we have to do it like
switch( variable )
{
   
case 0: case 1: case 2: case 3: case 4:
   
{
       
// ...
       
break;
   
}

   
case 5: case 6:
   
{
       
// ...
       
break;
   
}

   
default:
   
{
       
// ...
   
}
}

We have to use "case" for every, well, case when specifying a sequence, because it uses fallthrough. We have to use "break" to avoid that. I find this bad. Because of the necessity of using "break" we can't just "break" out of a loop if the switch is inside a loop. Also, if there are consecutive cases we have to specify each one of them. Additionally, we could also forget breaking. This "switch" isn't safe and is pretty old.

In Pawn we can do it a lot better:
switch( variable )
{
   
case 0 .. 4:
   
{
       
// ...
   
}

   
case 5, 6:
   
{
       
// ...
   
}

   
default:
   
{
       
// ...
   
}
}

1. There's no need for break, there's no fallthrough. Advantage: we can easily "continue" and "break" loops from inside the "switch".
2. We can use ".." if using several cases that are consecutive in the same block of code, to specify an interval.

I know that it's hard to have features accepted in such a popular standard, but upgrading switch is something that should really be considered ! C++17's [[fallthrough]] attribute is just as complicated as the "switch". It needs to be added everywhere or else we would get warnings (AFAIK).

C++ needs a lot more syntactic sugar. Small and nice things are making the language more likeable. I think that you should watch other languages too for interesting small features.

Thiago Macieira

unread,
Aug 1, 2017, 5:34:29 PM8/1/17
to std-pr...@isocpp.org
On terça-feira, 1 de agosto de 2017 14:24:04 PDT costeln...@gmail.com
wrote:
> We have to use "case" for every, well, case when specifying a sequence,
> because it uses fallthrough. We have to use "break" to avoid that. I find
> this bad. Because of the necessity of using "break" we can't just "break"
> out of a loop if the switch is inside a loop. Also, if there are
> consecutive cases we have to specify each one of them. Additionally, we
> could also forget breaking. This "switch" isn't safe and is pretty old.
>
> In Pawn we can do it a lot better:
> switch( variable )
> {
> case 0 .. 4:

With GNU extensions to C and C++:

case 0 ... 4:

> 1. There's no need for break, there's no fallthrough. Advantage: we can
> easily "continue" and "break" loops from inside the "switch".

That can't change now. This HAS been discussed in this list and in std-
discussions and, just like the non-local breaks, it's very hard to adapt the
language grammar to.

We need a different keyword or some modification keyword to the switch, since
there is unmarked code that expects to fall through. We cannot silently break
them.

> I know that it's hard to have features accepted in such a popular standard,
> but upgrading switch is something that should really be considered !
> C++17's [[fallthrough]] attribute is just as complicated as the "switch".
> It needs to be added everywhere or else we would get warnings (AFAIK).

We've done that in Qt. Not very difficult once the compiler starts warning.

The difficult part is to guess whether a fallthrough was intended or a break was
missing.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Vishal Oza

unread,
Aug 1, 2017, 5:53:28 PM8/1/17
to ISO C++ Standard - Future Proposals
I like the idea of adding new of adding to the switch statement but I think lets take it futher
switch (v)
{
case v= [1,5]: //incusive 1 to 5 1 <= v <= 5
Dosomething();
break;
case v = (5, 10): //exclusive range 5 < v < 10
doSomethingelse1();
break;
case v = (10, 50 ]: // 10 < v <= 50
doSomethingElse2();
break;
case v = (50,...) // 50 < v
doSomethingElse3();
break;
case v = 50:
doSomethingElse4();
break;
}
Valid for only if v is numeric also add multi variable values and strings

wei zhang

unread,
Aug 2, 2017, 2:19:39 AM8/2/17
to ISO C++ Standard - Future Proposals
We can only add silently with case 5, 6:




在 2017年8月2日星期三 UTC+8上午5:34:29,Thiago Macieira写道:

Bo Persson

unread,
Aug 2, 2017, 5:20:55 AM8/2/17
to std-pr...@isocpp.org
If you have large ranges, the compiler might translate that similar to
an if-statement anyway. Especially the (50,...) cannot easily be used in
a jump table.


Bo Persson


Bo Persson

unread,
Aug 2, 2017, 5:30:13 AM8/2/17
to std-pr...@isocpp.org
On 2017-08-01 23:24, costeln...@gmail.com wrote:
>
> I know that it's hard to have features accepted in such a popular
> standard, but upgrading switch is something that should really be
> considered ! C++17's [[fallthrough]] attribute is just as complicated as
> the "switch". It needs to be added everywhere or else we would get
> warnings (AFAIK).
>

In my experience it was just a handful of places in a (not that large)
code base. And most of those places already had a comment that the
fall-through was intentional.


Bo Persson


Matthew Woehlke

unread,
Aug 2, 2017, 10:58:42 AM8/2/17
to std-pr...@isocpp.org, costeln...@gmail.com
On 2017-08-01 17:24, costeln...@gmail.com wrote:
> 1. There's no need for break, there's no fallthrough. Advantage: we can
> easily "continue" and "break" loops from inside the "switch".

Disadvantage: you can't do this:

case FOO:
...some code...
[[fallthrough]];
case BAR:
...more code...
break;

And yes, I've actually written code like that before.

--
Matthew

Thiago Macieira

unread,
Aug 2, 2017, 11:00:26 AM8/2/17
to std-pr...@isocpp.org
On Wednesday, 2 August 2017 02:25:01 PDT Bo Persson wrote:
> In my experience it was just a handful of places in a (not that large)
> code base. And most of those places already had a comment that the
> fall-through was intentional.

There were a couple that didn't and those are the ones that took time to
investigate.

I think it was about 10 inside the Qt entire codebase (which OpenHub clocks at
7.9 million LOC).

Ross Smith

unread,
Aug 2, 2017, 5:18:40 PM8/2/17
to std-pr...@isocpp.org
There's only so much elaboration that could be added to the switch
statement before it stops offering any meaningful advantage in
convenience or readability over just manually writing the equivalent
sequence of if-elses.

Ross Smith

Cleiton Santoia

unread,
Aug 3, 2017, 12:29:01 AM8/3/17
to ISO C++ Standard - Future Proposals, ross....@otoy.com
There's only so much elaboration that could be added to the switch
statement before it stops offering any meaningful advantage in
convenience or readability over just manually writing the equivalent
sequence of if-elses.

My "pet switch"  would allow many conditions at once,and could be used in "not jump table" cases, like any object:

std::string x = "AF-56";
int k = 90;
enum YesNo{ NO,YES } answer;

switch ( answer, k, x.substr(0,2) ) {
case NO, default : // enters here if answer is NO and not check any of the others
   
break;
case YES, 10r.80r, default : // enters here if answer is yes and if k is in range 10-90, forget the third parameter
                             
// using UDL 'r' suffix and dot operator we may create a range that can compares thru operator == to a scalar
   
break;
case YES, 81, "XY" :  // no surprises here  
    break;
case YES, 82r.Inf, "XZ" :  // the range now is from 82 to infinity
    break;
case YES, 90, "AA"r."AX"r : // ranges can be of std::strings too ( or const char* ), why not ?

   
// This is actually the case that will be reached by (YES, 90, "AF")

   
break;
case default:
}


I know, comma may create problems, one may replace ',' with ';'

This was my 2 cents

olafv...@gmail.com

unread,
Aug 9, 2017, 7:55:38 AM8/9/17
to ISO C++ Standard - Future Proposals, costeln...@gmail.com
I'd like to see a generalization of switch, allowing it to be used with for example std::string (view).

std::string s;

switch (s)
{
case "A": 
case "B": 
default:
}

No syntax updates necessary.

Ville Voutilainen

unread,
Aug 9, 2017, 8:09:38 AM8/9/17
to ISO C++ Standard - Future Proposals, costeln...@gmail.com
That's not a switch, that's an if-else chain masquerading as a switch.
I would strongly
suggest adding a pattern-matching mechanism and leaving switch alone; switch has
semantics that don't fit into general matching, and other expectations
that a general
matching mechanism doesn't have, including recommendations against using switch
because switch does fall-through and a general matching mechanism
probably shouldn't.

gmis...@gmail.com

unread,
Aug 9, 2017, 9:08:05 AM8/9/17
to ISO C++ Standard - Future Proposals, costeln...@gmail.com

I would very much like to see some changes to switch.

I'd like something like:

switch break (x) // cases break by default;
{
    case 1: whatever();
    case 2: whatever();
}

I'd also like, for at least this variation of switch, for a case to be a scope by default, such that is ok:
So that variables can be declared without requiring more braces.

switch break (x) // cases break by default;
{
    case 1: int z = 1; whatever(z); // not an error
    case 2: whatever();
}

I think this would make most switch statements a lot less verbose.
It seems a lot of the flexibility of the classic switch just isn't required.
So this switch could be used when it's not and the existing switch can be used when it is.

switch break seems easy to teach to me. So why not?

Tony V E

unread,
Aug 9, 2017, 1:08:18 PM8/9/17
to Standard Proposals
break by default is fine, how do I opt-out within switch break, or is it all or nothing?

switch break(x)
{
case 1:
case 2:
    foo();
case 3:
    bar();
case 4:
    etc();
 }


How do I get fallthru on case 1 to 2?


P.S. I think the only way we get a new switch statement is by a big change - pattern matching.
No one wants to do incremental changes to switch.
But some of these questions need to be answered either way.

P.P.S. to the other question of


>
> switch (s)
> {
> case "A":
> case "B":
> default:
> }
>

I've seen C code do this, where s was a char *. (And s was typically pointing to static chars, like s = "B")
So you have a pointer s, which is just an int, right?
And all the cases are compile/link time constants...
I'm not sure that was valid C, but it worked.  Many years ago.

Tony


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/574320af-c1ae-4676-883c-ae4e91fcd874%40isocpp.org.



--
Be seeing you,
Tony

Olaf van der Spek

unread,
Aug 9, 2017, 1:40:40 PM8/9/17
to ISO C++ Standard - Future Proposals
2017-08-09 19:08 GMT+02:00 Tony V E <tvan...@gmail.com>:
>> switch (s)
>> {
>> case "A":
>> case "B":
>> default:
>> }
>>
>
> I've seen C code do this, where s was a char *. (And s was typically
> pointing to static chars, like s = "B")
> So you have a pointer s, which is just an int, right?
> And all the cases are compile/link time constants...
> I'm not sure that was valid C, but it worked. Many years ago.

Ah, so all I need to do is switch (s.c_str())? ;)

Thiago Macieira

unread,
Aug 9, 2017, 3:39:08 PM8/9/17
to std-pr...@isocpp.org
On quarta-feira, 9 de agosto de 2017 10:08:14 PDT Tony V E wrote:
> So you have a pointer s, which is just an int, right?
> And all the cases are compile/link time constants...
> I'm not sure that was valid C, but it worked. Many years ago.
>
> Tony

Something like this?

switch ("A") {
case "A":
return 0;
case "B":
return 1;
default:
return 2;
}

It doesn't compile in C: https://godbolt.org/g/m9Asnk (Clang) &
https://godbolt.org/g/Uy1pKW (GCC)

Even if it did, what do you think this would return? I'd say it's equally
correct to return 0 or 2.

Constantin-Flavius Nistor

unread,
Aug 9, 2017, 3:42:50 PM8/9/17
to ISO C++ Standard - Future Proposals
On Wednesday, 2 August 2017 09:19:39 UTC+3, wei zhang wrote:
We can only add silently with case 5, 6:

I know that, probably the ".." or "..." range thing too.


On Wednesday, 9 August 2017 16:08:05 UTC+3, gmis...@gmail.com wrote:

I would very much like to see some changes to switch.

I'd like something like:

switch break (x) // cases break by default;
{
    case 1: whatever();
    case 2: whatever();
}

I'd also like, for at least this variation of switch, for a case to be a scope by default, such that is ok:
So that variables can be declared without requiring more braces.

switch break (x) // cases break by default;
{
    case 1: int z = 1; whatever(z); // not an error
    case 2: whatever();
}

I think this would make most switch statements a lot less verbose.
It seems a lot of the flexibility of the classic switch just isn't required.
So this switch could be used when it's not and the existing switch can be used when it is.

switch break seems easy to teach to me. So why not?
I actually like this idea ! "switch break" could be just right for this. It wouldn't break backwards compatibility. 


On Wednesday, 2 August 2017 17:58:42 UTC+3, Matthew Woehlke wrote:
> 1. There's no need for break, there's no fallthrough. Advantage: we can 
> easily "continue" and "break" loops from inside the "switch". 

Disadvantage: you can't do this: 

  case FOO: 
    ...some code... 
    [[fallthrough]]; 
  case BAR: 
    ...more code... 
    break; 

And yes, I've actually written code like that before. 

-- 
Matthew 
 
On Wednesday, 9 August 2017 20:08:18 UTC+3, Tony V E wrote:
break by default is fine, how do I opt-out within switch break, or is it all or nothing?

switch break(x)
{
case 1:
case 2:
    foo();
case 3:
    bar();
case 4:
    etc();
 }


How do I get fallthru on case 1 to 2?
 
Well, for this we could use the current style switch. Or the [[fallthrough]] attribute could behave differently in a "switch break", it could enable the fallthrough there instead of only announcing it, if possible.


About the other too complex suggestions: I don't think that something similar to "case v = (50,...) // 50 < v" could be made reliably, I don't really know how jump tables really work, but I think that they work only for integers (so no possibility to use directly std::string ! probably only if it will get hashed into an int, but it will be faster than "if/else if/else" only if it would have many cases), without the possibility to actually check intervals, so a thing like "1..6" would actually expand to all those cases individually, it would only allow us to write it faster. It could give us a lot of benefits in these cases:

C++:
switch( someinteger )
{
   
case 581: case 523: case 462: case 521: case 463: case 522: case 461: case 448: case 468: case 586:
    case 509: case 481: case 510: case 472: case 473: case 493: case 595: case 484: case 430: case 453:
    case 452: case 446: case 454: case 590: case 569: case 537: case 538: case 570: case 449: case 513:
    case 520: case 519: case 487: case 417: case 406: case 556: case 444: case 557:
        return 1;
}

Pawn (my proposal):
switch( someinteger )
{
    case 581, 523, 462, 521, 463, 522, 461, 448, 468, 586, 509, 481, 510, 472, 473, 493, 595, 484, 430, 453,
         
452, 446, 454, 590, 569, 537, 538, 570, 449, 513, 520, 519, 487, 417, 406, 556, 444, 557:
             return 1;
}

And this could get a lot better, example:

C++:
// inside some function

for( unsigned int i = 0; i < liLength; i ++ )
{
    switch( lsString[ i ] )
    {
case '0': case '1': case '2': ...etc... case 'a': case 'b': ... etc ... case 'X': case 'Y': case 'Z':
case '[': case ']': case '(': case ')': case '$': case '@':
case '.': case '_': case '=': break;

default: return false;
}
}

Pawn (my proposal):
// inside some function

for
( new i = 0; i < liLength; i ++ )
{
    switch( lsString[ i ] )
    {
        case '0' .. '9', 'a' .. 'z', 'A' .. 'Z', '[', ']', '(', ')', '$', '@', '.', '_', '=': { }
        default: return 0;
    }
}

It would get expanded at compile time. It's a lot nicer.

Tony V E

unread,
Aug 9, 2017, 4:17:10 PM8/9/17
to Standard Proposals
On Wed, Aug 9, 2017 at 3:38 PM, Thiago Macieira <thi...@macieira.org> wrote:
On quarta-feira, 9 de agosto de 2017 10:08:14 PDT Tony V E wrote:
> So you have a pointer s, which is just an int, right?
> And all the cases are compile/link time constants...
> I'm not sure that was valid C, but it worked.  Many years ago.
>
> Tony

Something like this?

        switch ("A") {
        case "A":
                return 0;
        case "B":
                return 1;
        default:
                return 2;
        }

It doesn't compile in C: https://godbolt.org/g/m9Asnk (Clang) &
https://godbolt.org/g/Uy1pKW (GCC)

Even if it did, what do you think this would return? I'd say it's equally
correct to return 0 or 2.



yep, basically.

Once upon a time some compiler compiled that, and I agree, I wouldn't guarantee what it returned.
(I think the idea was that hitting the "A" case was an optimization, and case default was to do a strcmp())

This was more than 20 years ago.
Oh my. Maybe 30 years ago.
But that doesn't mean "more than 20" is incorrect, so I'll just keep telling myself "20", I've coded for 20+ years.

Tony

Thiago Macieira

unread,
Aug 9, 2017, 4:29:47 PM8/9/17
to std-pr...@isocpp.org
On quarta-feira, 9 de agosto de 2017 13:17:07 PDT Tony V E wrote:
> yep, basically.
>
> Once upon a time some compiler compiled that, and I agree, I wouldn't
> guarantee what it returned.
> (I think the idea was that hitting the "A" case was an optimization, and
> case default was to do a strcmp())

Like multicharacter literals?
int v = 'abcd';

GCC and ICC warn about that by default. Clang only warns at -Wpedantic. MSVC
does not warn even at -W4.

Tony V E

unread,
Aug 9, 2017, 4:37:12 PM8/9/17
to Standard Proposals
On Wed, Aug 9, 2017 at 4:29 PM, Thiago Macieira <thi...@macieira.org> wrote:
On quarta-feira, 9 de agosto de 2017 13:17:07 PDT Tony V E wrote:
> yep, basically.
>
> Once upon a time some compiler compiled that, and I agree, I wouldn't
> guarantee what it returned.
> (I think the idea was that hitting the "A" case was an optimization, and
> case default was to do a strcmp())

Like multicharacter literals?
        int v = 'abcd';

GCC and ICC warn about that by default. Clang only warns at -Wpedantic. MSVC
does not warn even at -W4.


No really, case "a literal string":
It was the wild west of compiling, before even C was a standard.

So really, maybe this isn't pertinent information to the current discussion, but it has been stuck in my head all these years.

Cleiton Santoia

unread,
Aug 9, 2017, 7:29:11 PM8/9/17
to ISO C++ Standard - Future Proposals, costeln...@gmail.com

That's not a switch, that's an if-else chain masquerading as a switch.
I would strongly
suggest adding a pattern-matching mechanism and leaving switch alone; switch has
semantics that don't fit into general matching, and other expectations
that a general
matching mechanism doesn't have, including recommendations against using switch
because switch does fall-through and a general matching mechanism
probably shouldn't.

This is good...
I want a *gmm* (general matching mechanism) and I think that the keyword for it should be switch.

Said that, now we need to a way to tell the compiler that we are talking about the "new" switch, maybe via positional token ( megazord ), analogous to "switch break" already mentioned. 

This switch will apply to more than one expression at a time, and use "==" for each element and "default break" unless you specify via [[fallthrough]]


std::string x = "AF-56";
enum YesNo{ NO,YES } answer = YesNo.YES;

switch megazord ( answer; int k = 90; x.substr(0,2) ) {
case NO; default : // enters here if answer is NO and not check any of the others
   
// Code
case YES; 10r.100r; "[_a-zA-Z][_a-zA-Z0-9]{0,30}"regex : // ranges and regular expressions, nice !
    // Code
case YES; 10r.100r; "AA"r."AX": // ranges can be of std::strings too ( or const char* ), why not ?
    // Code
case default:
}

I think this covers almost all problems:
1 - Can be used with std::string ( or anything else: std::variant, std::string_view, std::vector  ) 
2 - Can be used with ranges ( analogous to ".." operator ). I wonder... can I use two unary postfix "dot" operator in a sequence ?
3 - Can be used with many arguments at once.
4 - Execute functions, make temporaries and declare variables at the beginning
5 - Avoid unintentional fallthrough
6 - Leave the old switch alone

But still get the problem : 

switch megazord ("A") { // this is a const char * pointer and one cannot overload ''=="
}

We need

switch megazord("A"s) {  // <- string literal 
}



P.S. I know... I know... megazord is not a good name, may be 'batman' instead

gmis...@gmail.com

unread,
Aug 9, 2017, 9:13:12 PM8/9/17
to ISO C++ Standard - Future Proposals


On Thursday, August 10, 2017 at 5:08:18 AM UTC+12, Tony V E wrote:
break by default is fine, how do I opt-out within switch break, or is it all or nothing?

switch break(x)
{
case 1:
case 2:
    foo();
case 3:
    bar();
case 4:
    etc();
 }


How do I get fallthru on case 1 to 2?

I would not object to fallthrough being a keyword like so (but see later):

switch break(x)
{
case 1: fallthrough;

case 2:
    foo();
case 3:
    bar();
case 4:
    etc();
 }

But I might prefer - in the absence of some range of values syntax - to make an empty case automatically fallthrough;
and if you don't want to fall through use an empty statement as shown below, because the need is common and it's much briefer.

switch break(x)
{
case 1: ; // Don't fall through.

case 2:
    foo();
case 3:
    bar();
case 4:
    etc();
 }
And if you want to fallthrough in the middle of some case, just use goto. Which I think is clearer.
And if you are using fallthrough that often, just use switch not switch break.

What do you think?

Bo Persson

unread,
Aug 9, 2017, 10:23:21 PM8/9/17
to std-pr...@isocpp.org
On 2017-08-10 03:13, gmis...@gmail.com wrote:
>
>
> On Thursday, August 10, 2017 at 5:08:18 AM UTC+12, Tony V E wrote:
>
> break by default is fine, how do I opt-out within switch break, or
> is it all or nothing?
>
> switch break(x)
> {
> case 1:
> case 2:
> foo();
> case 3:
> bar();
> case 4:
> etc();
> }
>
>
> How do I get fallthru on case 1 to 2?
>
>
> I would not object to fallthrough being a keyword like so (but see later):
>
> switch break(x)
> {
> case 1: fallthrough;
> case 2:
> foo();
> case 3:
> bar();
> case 4:
> etc();
> }
>

That would make it really experts-only, as we already have the current
version

switch (x)
{
case 1: [[fallthrough]];
case 2:
foo();
case 3:
bar();
case 4:
etc();
}


How many variations do we need?


Bo Persson



Tony V E

unread,
Aug 9, 2017, 10:45:27 PM8/9/17
to Standard Proposals
Well, the [[fallthrough]] one, in some sense, doesn't really exist, because attributes are ignorable and don't change anything.

I don't expect to ever get "switch break (x)", but I do expect we eventually get pattern matching that subsumes switch.  Maybe "switch [x]", since square brackets are in style? Or throw in an auto or something.  Or it is called "inspect" (that's the name Bjarne used).

When we do get "inspect", it we almost certainly break by default.  I think we will still _probably_ need something to do fallthrough - and it can't be an attribute.
Or maybe we say fallthrough really isn't worth it.


Reply all
Reply to author
Forward
0 new messages