7 ms·
Cdecl – Turns English phrases into C declarations
- Eli_P 8y agoIf I recall correctly, this one came as an exercise in Knuth's book of C programming, ibidem were C declarations and priorities explained.
- userbinator 8y agoThe K in K&R is for (Brian) Kernighan, not Knuth. Knuth does not use C in his books.
- saagarjha 8y agoFor 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.
- jcranmer 8y agoHere'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).
- Hex08 8y ago*An array of 4 ints Great explanation though, it really helps to read things inside-out
- ramshorns 8y agoWhich part are you correcting? The declaration "int bar[3];" is an array of 3 ints, which are bar[0], bar[1] and bar[2]. Declaration mimics use but it's not exactly the same; in this case the size replaces the indices, which are all less than it.
- deleted 8y ago[deleted]
- exitcode00 8y agoHow 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 8y agoIn 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?
- 8y ago
- deleted 8y ago[deleted]
- mey 8y agoNeed this for bash and by proxy regex.
- bewuethr 8y agoThere is https://explainshell.com https://explainshell.com - not Bash specific, though.
- drewsberry 8y agoThere's https://regex101.com/ https://regex101.com/ for regex, does an excellent job, can't really ask for more
- Jerry2 8y agoTried it on this gibberish but it complains about syntax: ((void(*)(void))0)();
- deleted 8y ago[deleted]
- tntn 8y agoThat's not a declaration.
- poizan42 8y agoBesides 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.
- TorKlingberg 8y agoI'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 8y agoCould you not substitute in a known type, get the result and insert the unknown type back in afterwards?
- skookumchuck 8y agoThe way to make complex C declarations legible is to use typedefs for the subtypes (like function pointers).
- ridiculous_fish 8y agoHey, 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.
- erroneousboat 8y agoThanks for creating it, it really helps with learning C.
- buboard 8y agoDidnt know what blocks are , it seems it's an apple extension.
- saagarjha 8y agoYup, 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...
- kitd 8y agoNice work. Could you run it as a preprocessor macro?
- SidiousL 8y agoThe 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".
- pkaye 8y agoIts better to create a series of typedef and build up the declaration. Most of the time you need those sub typedef anyway.
- nwmcsween 8y agoExtremely sparingly, typedefs like in glib are a nightmare and just arbitrary typedefs like char -> char_t are just useless
- nurettin 8y agoBack 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.
- unnouinceput 8y agotried: 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 8y agoYou are mixing type declarations and values. Foo equals bar is not a constraint that can be specified via the type system (generally speaking).
- deleted 8y ago[deleted]
- valerij 8y agoon topic of function pointers, is there a template to turn std::funtion<foo(bar, baz)> into foo(*)(bar, baz) ?