[Day 214] The Comma Operator & the Customs of 1972

Hi all,

The compile errors that we see in episode 214 at 14:46 are probably worth talking about since there's a good chance you'll see lots of other errors with related causes over the years.

Short version:
At 14:46, the "sequence operator" is not an operator at all; it's a declarator separator. You can fix it by surrounding everything from = to ; with parentheses.

---

Longer version:

There's two things in play here: (1) whether the compiler regards the comma in question as an operator (it doesn't) and (2) how parentheses can be used in a declaration.

Declaration syntax allows for parentheses around the name of the thing being declared; e.g. this:
1
int n = 42;
...means the same thing as
1
int (n) = 42;
The parentheses here are just like the parentheses you would use when declaring a pointer-to-function, except that the * has been left out. (And also, we don't have a list of function parameters.)

This is because of how Dennis Ritchie defined the grammar term "declarator" (which you'll find in the PDF version of "The C Reference Manual" (31 pages), linked from his page at Bell Labs). If you look, you'll see that a declarator can be a parenthesized declarator. He also motivates this inside-out syntax on the same page.

(Finding the grammar terms that indicate the meaning of the comma is left as an exercise for the reader. See also the grammar definitions in [expr], [dcl.dcl] and [dcl.decl] in the C++ working paper.)

You might ask, "what do parenthesized declarators have to do with Casey's code?"

On the macro definition line, in:
1
/* [...] */  #Path) , (DebugValue##Path.Value_b32 = /* [...] */ 

... here's what's happening: because that particular comma marks the end of the initializer expression to its left, everything to the *right* of the comma introduces a new variable. So the left-parenthesis in
1
(DebugValue##Path.Value_b32 = 
opens a parenthesized declarator (just like with "(n) = 42" above) and introduces a declaration of a variable named "DebugValueRenderer_ShowLightingSamples" for the second time on this line.

So now, MSVC's errors should make sense: starting at the left paren, it thought it was going to see something like:

1
debug_event (DebugValueRenderer_ShowLightingSamples);

... which would mean the same thing as:
1
debug_event DebugValueRenderer_ShowLightingSamples;
But instead, it saw a . in place of the right-paren, hence the error.

In fact, you can make it give you the same error with just:
1
int (n.;

You might ask why Ritchie designed the grammar so that commas would be treated differently in the context of a variable definition.

I'm not sure it's merely because of his personal preference: remember, in 1972, code had to be entered by hand with binary toggle switches and punch cards on a machine with something like 12 kB of RAM for userspace programs (twelve kilobytes!). So it made sense to be extremely concise.

So this:
1
2
3
char c;
char *s
char **ps;
...would have been much more of a pain to enter and work with than:
1
char c,*s,**ps;