3

I'm reading boost/asio/coroutine.hpp and cannot understand the implementation of BOOST_ASIO_CORO_REENTER and BOOST_ASIO_CORO_YIELD. The expanded form of

reenter (this) {
  yield ..
  yield ..
}

seems to be intertwined switch/if/for statements. I'm wondering how come this is valid C code? I wrote something similar (shown below) and found it compiles using gcc.

int main() {
  int a = 1;
  switch (a)
  case 0: if (1) a = 2;
  else case 1: for (;;) {
    case 3:
      break;
  }

  return 0;
}
user207421
  • 305,947
  • 44
  • 307
  • 483
PeopleMoutainPeopleSea
  • 1,492
  • 1
  • 15
  • 24
  • 6
    That is some [Duff's device](https://en.wikipedia.org/wiki/Duff%27s_device) trickery. – ezegoing Jan 06 '20 at 08:39
  • 3
    If you are using boost, then you are writing C++, not C. So you should be asking why this is valid C++, not why it is valid C (even if the answers would probably be quite similar). – walnut Jan 06 '20 at 08:41
  • Care to include some of the original code ? – darune Jan 06 '20 at 08:53
  • [switch statement with do-while interleaved](https://stackoverflow.com/q/9595242/995714), [How does Duff's device work?](https://stackoverflow.com/q/514118/995714) – phuclv Jan 06 '20 at 09:06
  • This has exacfly nothing to do with [tag:boost-asio]. It is a syntax question. Don't tag indiscriminately. – user207421 Jan 06 '20 at 09:36

2 Answers2

3

The reason is because switch statements aren't structured control flow statements. Instead, they should be considered as syntactic sugar for static dispatch. Dispatch means that the control flow is redirected and static means that the compiler knows where it is redirected to.

So your code

int a = 1;
switch (a)
case 0: if (1) a = 2;
else case 1: for (;;) {
  case 3:
    break;
}
return 0;

would be compiled into something roughly equivalent of

int a = 1;
void *dest = dispatch(a, { case0_addr, case1_addr, case3_addr });
goto *dest;
case0_addr:
if (1) { 
  a = 2;
} else {
case1_addr:
  for (;;) {
    case3_addr:
    goto case_end;
  }
}
case_end:
return 0;

where dispatch is a function the compiler runs for emitting the machine code required for the static dispatch. Since all dispatch values are constants and all dispatch targets are known by the compiler, it can generate very efficient machine code.

As for why it is legal, I guess the reason is because there's no particular reason for it to be illegal. As shown, case statements are just goto labels so they can be placed wherever.

Björn Lindqvist
  • 19,221
  • 20
  • 87
  • 122
2

Syntactically, the body of a switch is just a statement (usually, but not necessarily a compound statement)

6.8:

statement:
               labeled-statement
               compound-statement
               expression-statement
               selection-statement
               iteration-statement
               jump-statement

which may be labeled 6.8.1:

labeled-statement:
                identifier : statement
                case constant-expression : statement
                default : statement

Example:

switch(1) one: case 1: dothis();

If it is a compound statement, then each substatement recursively may also be labeled. Example:

switch(x) {
    if(1) one: case 1: dothis();
    else case 0: orthis();  /*fallthru*/
    three: case 3: three();
}

The syntax treats case/default-labels and regular labels the same, only the semantic check verifies that case/default-labels be inside a switch.

Implementation-wise, everything compiles into (flat) assembly.

E.g.

if(test) YesBranch; else ElseBranch;

is flattened into (pseudo-assembly)

IF_NOT_THEN_GOTO(test, PAST_YES_BRANCH)
YesBranch
goto PAST_NO_BRANCH;
NoBranch
PAST_NO_BRANCH:;

and there's no reason why anything in such flat code couldn't be labeled.

case/default labels are also just like regular labels except they're also used in (most usually) a computed jump.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142