Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The spiral rule about declarations — when is it in error?

Tags:

c

declaration

I recently learned the spiral rule for deobfuscating complex declarations, that should have been written with a series of typedefs. However, the following comment alarms me:

A frequently cited simplification, which only works for a few simple cases.

I do not find void (*signal(int, void (*fp)(int)))(int); a "simple case". Which is all the more alarming, by the way.

So, my question is, in which situations will I be correct to apply the rule, and in which it would be in error?

like image 413
Vorac Avatar asked Apr 28 '13 06:04

Vorac


People also ask

What is spiral rule?

The “spiral rule” is just an approximation of the actual rule as defined in the standard: declaration follows usage. Even with typedefs, that declaration means “when you call baz with a bing and a pointer (named bratz) to a function of type boff(biff), then you get back a pointer to a function of type foo(buff).”

What is spiral syntax?

The Spiral/Clockwise Method is a magic tool for C/C++ programmers to define the meaning of syntax declaration in the head within seconds. This method was created by David Anderson and here is a short brief about how to apply this method.


1 Answers

Basically speaking, the rule simply doesn't work, or else it works by redefining what is meant by spiral (in which case, there's no point in it. Consider, for example:

int* a[10][15];

The spiral rule would give a is an array[10] of pointer to array[15] of int, which is wrong. It the case you cite, it doesn't work either; in fact, in the case of signal, it's not even clear where you should start the spiral.

In general, it's easier to find examples of where the rule fails than examples where it works.

I'm often tempted to say that parsing a C++ declaration is simple, but nobody who has tried with complicated declarations would believe me. On the other hand, it's not as hard as it is sometimes made out to be. The secret is to think of the declaration exactly as you would an expression, but with a lot less operators, and a very simple precedence rule: all operators to the right have precedence over all operators to the left. In the absence of parentheses, this means process everything to the right first, then everything to the left, and process parentheses exactly as you would in any other expression. The actual difficulty is not the syntax per se, but that it results is some very complex and counterintuitive declarations, in particular where function return values and pointers to functions are involved: the first right, then left rule means that operators at a particular level are often widely separated, e.g.:

int (*f( /* lots of parameters */ ))[10];

The final term in the expansion here is int[10], but putting the [10] after the complete function specification is (at least to me) very unnatural, and I have to stop and work it out each time. (It's probably this tendency for logically adjacent parts to spread out that lead to the spiral rule. The problem is, of course, that in the absence of parentheses, they don't always spread out—anytime you see [i][j], the rule is go right, then go right again, rather than spiral.)

And since we're now thinking of declarations in terms of expressions: what do you do when an expression becomes too complicated to read? You introduce intermediate variables in order to make it easier to read. In the case of declarations, the "intermediate variables" are typedef. In particular, I would argue that any time part of the return type ends up after the function arguments (and a lot of other times as well), you should use a typedef to make the declaration simpler. (This is a "do as I say, not as I do" rule, however. I'm afraid that I'll occasionally use some very complex declarations.)

like image 197
James Kanze Avatar answered Oct 09 '22 00:10

James Kanze