

The “Clockwise/Spiral Rule” - dsr12
http://c-faq.com/decl/spiral.anderson.html

======
stephencanon
I link to my old response every time the "spiral rule" comes up. It makes for
pretty pictures, but it's nonsense.

[https://news.ycombinator.com/item?id=5080096](https://news.ycombinator.com/item?id=5080096)

------
StephenFalken
C was created for solving a very practical and specific problem: making the
newly born UNIX operating system easily portable across different
architectures. It was an impressive success on that challenge.

That means most of the decisions Dennis Ritchie (with the help of others) took
when designing the language were the right ones for solving the problem at
hand [1], otherwise the language wouldn't have survived as strongly as it did.
[2]

[1] [http://cm.bell-labs.com/who/dmr/chist.pdf](http://cm.bell-
labs.com/who/dmr/chist.pdf)

[2]
[http://www.tiobe.com/index.php/content/paperinfo/tpci/index....](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)

~~~
nkurz
If you happen to have the whole afternoon free, this 850+ post thread on
comp.arch eventually leads to some wonderful discussion of how modern C has
(for better or worse) changed from this original mission to "something else":
[http://compgroups.net/comp.arch/if-it-were-
easy/2993157](http://compgroups.net/comp.arch/if-it-were-easy/2993157)

~~~
slashnull
Holy cow this looks awesome

------
MereInterest
Whenever using C++11, I try to use std::function everywhere instead of
function pointers, because it ends up being so, so much more readable. Taking
the article's last example, I rewrite it using std::function.

    
    
        void (*signal(int, void (*fp)(int)))(int);
        function<function<void(int)> (int, function<void(int)>) > signal;
    

It is more verbose, but easier to read, in my opinion. The return value of
signal, function<void(int)>, is in one place, instead of being split into the
beginning and end of the expression.

As an added bonus, it can also handle lambda functions or bound methods, which
the function pointer version cannot.

~~~
evincarofautumn
std::function has runtime overhead, though. Maybe you’d prefer a type alias
with the same syntax as std::function?

    
    
        template<class F>
        struct func_ {};
    
        template<class Result, class... Args>
        struct func_<Result(Args...)> {
          typedef Result(*type)(Args...);
        };
    
        template<class F>
        using func = typename func_<F>::type;
    
        func<func<void(int)>(int, func<void(int)>)> signal;

~~~
MereInterest
Ooh, I hadn't thought of that. I will generally use a templated form if I care
about the runtime overhead, or a std::function if I want to support multiple
types of callables within the same container, and so I don't use raw function
pointers very often.

~~~
evincarofautumn
By the way, I’m an idiot.

    
    
        template<class T>
        using ptr = T*;
    
        ptr<ptr<void(int)>(int, ptr<void(int)>)> signal;
    

:)

------
marcoms
Be careful that you don't trip up using this rule for everything - its not
always correct [0]

[0] [http://stackoverflow.com/questions/16260417/the-spiral-
rule-...](http://stackoverflow.com/questions/16260417/the-spiral-rule-about-
declarations-when-is-it-in-error)

------
jberryman
It's helpful to start from K&R Appendix A8.6: "A declarator is read as an
assertion that when its identifier appears in an expression of the same form
as the declarator, it yields an object of the specified type."

------
nraynaud
this is the kind of thing that led to my mantra: use C as least as you can get
with.

The sad truth is that it leaves mostly the embedded world, where you are stuck
with C, you have to deal with GCC, you have to deal with cross compilation GCC
and you have no real debugging facility. The place where you would want to
avoid bugs, and the place where finding a bug is a pain and undefined behavior
lurks at every street corner.

------
_almosnow
What? No... Just print some operator precedence chart [0] and follow it; it is
much easier than it looks.

[0]
[http://en.cppreference.com/w/cpp/language/operator_precedenc...](http://en.cppreference.com/w/cpp/language/operator_precedence)

------
alongub
This technique helped me figure out
[http://stackoverflow.com/q/25671410/140937](http://stackoverflow.com/q/25671410/140937)
in seconds. This is gold. Thank you!

------
leaveyou
It was a very sad day for computing when Ritchie released this brain fart upon
the world. I think this "invention" called spiral rule is one significant
reason why so many good programmers who know or knew C at one point don't use
C in their projects and will never contribute to open source projects written
in C.

~~~
outworlder
Nowadays, it seems difficult to understand how could someone invent something
so... awkward. However, keep in mind all the other constraints he had at the
time. Inventing a sane syntax probably took a backseat ( _)

Now, the bigger issue is why is the C syntax surviving. I blame Java and now
Javascript. This will only get worse as more people get to know it. At least
javascript doesn't have so many type shenanigans: try parsing in your head a
pointer to a C function that takes a pointer to a struct or something.

Still, C is a pretty nice fallback when you have nailed your algorithm and
must absolutely get the last drop of performance (and want to still reason
about what the compiler is doing, so C++ is out). Or when you want to
interface with anything written in C.

(_) Then again, he could just have done a Lisp and removed almost all syntax.

~~~
xg15
I'm no C expert, but couldn't you contain the madness for a good part with a
sane code convention and smart use of typedefs? It looks to me as if the part
where everything breaks down is mostly with "nested" function pointer types
like in the "ultimate" example of the tutorial. (The syntax of "simple"
function pointers is quirky as well, but without recursion, you might get the
hang of it after a short time)

So, couldn't you avoid much of the confusion by mandating that function
pointers may refer to other function pointers only through typedefs?

Of course this wouldn't work for legacy code...

~~~
outworlder
Yes, that makes it slightly saner by not having to parse the declaration in
your head all the time.

