
Cdecl – Turns English phrases into C declarations - dammitcoetzee
https://cdecl.org/
======
jcranmer
Here's an easy way to understand how these things work: in C, the type of a
pointer/function/array mess is declared by how it's used. For a declaration
like "int ( * ( * foo)(void))[3]", you can read it as "for a variable foo,
after computing the expression ( * ( * foo)(void))[3], the result is an int."

So one way to read C "gibberish" is to ignore the type at the beginning and
parse the rest as an expression like a normal parse tree. First we take foo.
Then we dereference it (so foo is a pointer). Next we call it as a function
with no arguments (so foo is a pointer to a function that takes no arguments).
Next, we dereference it again. Then we index into the result as an array.
Finally, we reach the end, so we look at what the declared type and find that
this type is an int. So foo is a pointer to a function that takes no arguments
and returns a pointer to an array of 3 ints.

You can also use this to go backwards. What's the syntax for a function that
takes an integer argument returns a pointer to an array of function pointers
taking no arguments and returning integers? Well, we want to take foo, call
it, dereference it, then index into an array, then dereference it again, then
call it again, then return an int. Or int (* (* (foo)(int))[5])(void).

~~~
exitcode00
How about just using Ada? It has the added bonus of not being a gimmick
(depending on who you ask I suppose ; )

Ada: type Ret_Typ is array (1..3) of Integer; Foo : access function return not
null access Ret_Typ := null;

C: int ((foo)(const void *))[3]

Cdecl: declare foo as pointer to function (pointer to const void) returning
pointer to array 3 of int

~~~
tomjakubowski
In the interest of furthering annoying language smuggery, the rough Rust
equivalent:

    
    
        foo: fn() -> Box<[i32; 3]>
    

Alternately, if the pointer is into static memory and not something allocated
on the heap:

    
    
        foo: fn() -> &'static <[i32; 3]>;
    

That's pretty nice to look at and not too hard to read. In my opinion, for
commonly used syntax (like fn decls), some well-chosen punctuation marks (',
->, :, in this case) are often boon to readability compared to keywords. So I
think the Rust syntax in this case is nicer than Ada's.

But in any case, while complicated C declarations may be uglier and take more
effort to read than those in other languages, they are at least tractable once
you learn the trick of "declaration follows use" and working backwards as GP
describes.

Separately, though, what do you mean by your "gimmick" comment?

~~~
exitcode00
> Separately, though, what do you mean by your "gimmick" comment?

Just meaning the website CDecl - its a neat tool to make C readable in
English, but Ada is a real language used to make planes fly etc. Many people
have contempt for it though which is why I made the joke xD

Interesting that you can note which type of memory an anonymous type comes
from in Rust. I suppose its for optimization purposes? Doesn't seem that
helpful from a pure typing perspective.

As an aside I doubt a layman would be able to understand that notation in
Rust, whereas my girlfriend might be able to grasp or read Ada code or the
output of CDecl.

------
ridiculous_fish
Hey, this is my site, first published 2009! This is the venerable cdecl
enhanced with blocks support.

It used to be a shared host with a PHP script shelling out to the cdecl
executable, written in K&R C. Now it's that same executable running on AWS
Lambda.

Yes Lambda really will run arbitrary ELF binaries.

~~~
buboard
Didnt know what blocks are , it seems it's an apple extension.

~~~
saagarjha
Yup, they’re an extension to C/Objective-C/C++ implemented in Clang:
[https://en.m.wikipedia.org/wiki/Blocks_(C_language_extension...](https://en.m.wikipedia.org/wiki/Blocks_\(C_language_extension\))

------
saagarjha
For all of its simplicity, the syntax for complex types in C is pretty
horrible. Yes, I know the "inside out" rule, and can usually read these, but
that doesn't make it any less bad.

------
TorKlingberg
I've been a professional C programmer for years, but I rarely find cdecl
useful (command line or website). Not because complex C declarations are
intuitive to me, but because cdecl fails on any unknown types. Real world C
code is full of typedefs.

~~~
kitd
Could you not substitute in a known type, get the result and insert the
unknown type back in afterwards?

------
nurettin
Back in early 2000s, we had bots on IRC doing this. My favorite technique was
to pass the type to a template function, assign it to an integer and then
parse the compile time error produced by gcc to extract the type.

------
Eli_P
If I recall correctly, this one came as an exercise in Knuth's book of C
programming, ibidem were C declarations and priorities explained.

~~~
userbinator
The K in K&R is for (Brian) Kernighan, not Knuth.

Knuth does not use C in his books.

------
pkaye
Its better to create a series of typedef and build up the declaration. Most of
the time you need those sub typedef anyway.

~~~
nwmcsween
Extremely sparingly, typedefs like in glib are a nightmare and just arbitrary
typedefs like char -> char_t are just useless

------
skookumchuck
The way to make complex C declarations legible is to use typedefs for the
subtypes (like function pointers).

------
valerij
on topic of function pointers, is there a template to turn

    
    
      std::funtion<foo(bar, baz)>
    

into

    
    
      foo(*)(bar, baz)
    
    ?

~~~
bartbes
Sure. Here's one: [https://godbolt.org/z/vWl4NE](https://godbolt.org/z/vWl4NE)

~~~
valerij
huh. this was easier than expected

------
mey
Need this for bash and by proxy regex.

~~~
bewuethr
There is [https://explainshell.com](https://explainshell.com) \- not Bash
specific, though.

------
SidiousL
The actual principle behind the C type declarations is "declaration follows
use". Let me explain what this means. Take this declaration

    
    
       int *pi;
    

Means that when I dereference the variable pi, I get an int. This also
explains why

    
    
       int *pi, i;
    

declares `pi` as a pointer to `int` and `i` as an `int`. From this point of
view it makes sense stylistically to put * near the variable.

Declaration of array types is similar. For example,

    
    
       int arr[10];
    

means that when I take an element of `arr`, I obtain an `int`. Hence, `arr` is
an array of ints.

Pointers to functions work the same way. For example,

    
    
       int (*f)(char, double);
    

means that if I dereference the variable `f` and I evaluate it on a `char` and
on a `double`, then I get an `int`. Hence, the type of `f` is "pointer to
function which takes as arguments a char and a double and returns an int".

------
Jerry2
Tried it on this _gibberish_ but it complains about syntax:

((void(*)(void))0)();

~~~
poizan42
Besides not being a declaration it really is gibberish - calling a null
pointer is undefined behaviour. I believe the correct way of calling a
function at address zero is ((void( _)(void))(intptr_t)0)(); which is merely
implementation defined.

C has the weird thing that a literal zero in a pointer context becomes a null-
pointer which may not actually have the bit pattern 0. And when the optimizer
sees a guaranteed null pointer it tends to optimize the whole branch away
since entering that would be UB. So if you try calling address zero with
"((void(_)(void))0)();" you might end up with the whole function optimized
away as well as any function it gets inlined into.

------
unnouinceput
tried: declare xxx as integer pointer to array of string equal to "mumu" and
"kaka"

got: bad character '"'...apostrophe instead of double quote has the same
result...well, I guess I expected too much

~~~
ComputerGuru
You are mixing type declarations and values. Foo equals bar is not a
constraint that can be specified via the type system (generally speaking).

