I'm more of the mindset that writing something like this is probably a code smell to begin with. Is there any reason I'm not thinking of right now, that this couldn't be typedef'd and refactored into something far more readable?
C gets a lot of blame for pointer gibberish like this but quite honestly you can write gibberish in any language. I don't see any fundamental or technical reason you couldn't write clean, readable C.
Why do people continue to use this term "code smell", instead of "hard to read code" or something similar, more equivalent??? First seem almost offensive to an author.
“Code smell” does not mean “hard to read code”, it means “an unreliable but still useful indication that there may be something wrong in the design of the code connected to the piece described as having the ‘smell’”.
And the reason it continues to be used is that it is a concise idiom that is useful.
"an unreliable but still useful indication that there may be something wrong in the design of the code"
Well, but you just proved the point - it can be described in non offensive manner. And more over, judging something based on gut feeling ("indication") may be actually even worse, as you may offend someone who did actually a good job.
Sorry if "nit picking", recently was reading a lot about burn outs in the industry, and this is a thing that did catch my attention...
It's "smell" in the same sense as "something smells fishy here", or "I smell trouble", with smell serving the analogy of being the least specific of your senses, alluding to having a non-specific feeling rather than hard evidence about something. In theory there's no implication of a "repulsive / offensive smell" or "ew this code smells" in the phrase, like you seem to perceive it.
Granted, these things are subjective, but it's similar to complaining about the term black humour being racist, when black in this context is not meant to have any racial context.
Agree, these are subjective, at the same time, when I think of it again, from technical standpoint such a comment or CR would be very low quality comment/CR.
I don't keep up with the latest C shenanigans, cuz I like C the way it was, but have they change something where "pointer to an array[5]" is a meaningful distinction to draw?
I mean, "a pointer to an array of X" is simply "a pointer to X" and using hungarian notation, you can encode the knowledge "this pointer can be incremented" into its name.
and typedef'ing *function declarations? who has families of functions with the same type signatures that they want to point to?
> but have they change something where "pointer to an array[5]" is a meaningful distinction to draw
This has been a meaningful distinction since at least C99.
> "a pointer to an array of X" is simply "a pointer to X"
They aren't actually the same. The former can be decayed into the latter but they aren't actually equivalent and you'll get type mismatches if you try to treat them as the same thing.
> who has families of functions with the same type signatures that they want to point to
An example is callbacks or handlers for responding to interrupt requests. A lot of hardware interface code relies on typedef-ed function decls because very often you are passing user side functions through your interface so that you can stash those function pointers somewhere and invoke them when some event occurs.
that's an array of 3 pointers to ints. if you pass foo as an argument you get a pointer to a pointer to an int (with knowledge if you can hang onto it that there are more pointers to ints lined up in memory)
The trick is to just assert equal types. Most compilers have extensions that allow you to easily compare type equality (and C23 actually standardizes the `typeof()` operator).
So you basically take your ugly type, put it in a #define and then create a static assertion that matches the type against said ugly type.
Now the compiler will throw a shit fit if the types don't match. Have fun breaking the type up into smaller pieces until you have something legible.
Rust isn't so bad, is it? The example of `char ((x[3])())[5]` would translate to `[fn() -> [fn() -> u8; 5]; 3]`. It's inherently an ugly type, but I think it's easier to read than the C version.
I don't think it is gibberish. It's code and in order to read that code you need to understand the language, and to understand language you need learning and experience.
Maybe it can be useful for learning, but if you have to use such tool, I suspect you won't understand it anyway - so in a way it is more a gibberish-to-gibberish translator.
I disagree. The conventions for declaring arrays, pointers, and function pointers are all idiosyncratic. In C, the type is always to the left of the variable being declared. Except for arrays, which have part of the declaration to the right. And except for pointers, which need to be affixed to every item if there are multiple declarations. And except for function pointers where you need to wrap the variable name like (*name). Individually I can wrap my head around these exceptions, but putting all of them together, it's just hard to read.
It takes a pretty smart person to do that. Which is pretty confusing.
How can such a smart person not not understand how all things that are possible are not all equally good?
The fact that both the compiler and you can parse that doesn't make it a good way to document or convey meaning or intent.
C is chock full of inconsistencies and ambiguities that are only disambiguated by essentially being a human compiler and maintaining the same parsing state-machine manually in your head to know what any given "(" actually means or does. As a self-proclaimed fluent C linguist, you know this better than most.
All coding involves that of course but all implimentations are not equally unhelpful.
The cpu and some people can read the binary itself. They just need to know the cpu's opcodes, documented right in the datasheet that anyone can read.
var x: pointer to func() pointer to array[5] of pointer to func() char;
or if you wish to replace some keywords with glyphs:
var x: ^func() ^[5] ^func() char;
And it's always a nice puzzle for the reader to explain why there are three "pointer" in cdecl output and three carets in the ALGOL-like declaration, but only two asterisks in the C declaration.
In this case, the C declaration doesn't match the other two. The variable x is a function that returns a pointer to an array of 5 pointers to functions returning char. Indeed, that's what cdecl.org says:
declare x as function returning pointer to array 5 of pointer to function returning char
Using the notation you did, that would be:
var x: func() ^[5] ^func() char
There are only two arrows there now.
If you wanted a pointer to a function like this, you would need a third asterisk in the declaration:
Oh, good catch, thank you! But I remember an example with some other tricky C expression/type declarator where the number of actual dereferences differed from the amount of asterisks in the code.
> Using the notation you did, that would be:
Well, it'd be
func x() ^[5] ^func() char; ...
because it's a function declaration, after all, not a variable.
> But I remember an example with some other tricky C expression/type declarator where the number of actual dereferences differed from the amount of asterisks in the code.
I was thinking of the issue of going from English to C. Even when you out in malformed C it's nice to get either a compiler error which explains the problem, or even better an LLM that can suggest how to fix it.
C gets a lot of blame for pointer gibberish like this but quite honestly you can write gibberish in any language. I don't see any fundamental or technical reason you couldn't write clean, readable C.
Why do people continue to use this term "code smell", instead of "hard to read code" or something similar, more equivalent??? First seem almost offensive to an author.
“Code smell” does not mean “hard to read code”, it means “an unreliable but still useful indication that there may be something wrong in the design of the code connected to the piece described as having the ‘smell’”.
And the reason it continues to be used is that it is a concise idiom that is useful.
"an unreliable but still useful indication that there may be something wrong in the design of the code"
Well, but you just proved the point - it can be described in non offensive manner. And more over, judging something based on gut feeling ("indication") may be actually even worse, as you may offend someone who did actually a good job.
Sorry if "nit picking", recently was reading a lot about burn outs in the industry, and this is a thing that did catch my attention...
I don't think it's meant to be offensive.
It's "smell" in the same sense as "something smells fishy here", or "I smell trouble", with smell serving the analogy of being the least specific of your senses, alluding to having a non-specific feeling rather than hard evidence about something. In theory there's no implication of a "repulsive / offensive smell" or "ew this code smells" in the phrase, like you seem to perceive it.
Granted, these things are subjective, but it's similar to complaining about the term black humour being racist, when black in this context is not meant to have any racial context.
Agree, these are subjective, at the same time, when I think of it again, from technical standpoint such a comment or CR would be very low quality comment/CR.
An author is not their work.
People should not be offended if others critique their work in good faith.
Nobody's fragile ego is worth censoring discussion seeking truth.
Code smell is something that implies deeper issues but isn't usually a serious issue by itself.
I am sorry if you've been assaulted by bad code reviews, but a 'code smell' is a useful idea and term.
I'm not a C programmer but your comment reminds me of all the lame jokes people make about the German language.
the function signature certainly should be type def-ed. i.e.
and then you have the original asI don't keep up with the latest C shenanigans, cuz I like C the way it was, but have they change something where "pointer to an array[5]" is a meaningful distinction to draw?
I mean, "a pointer to an array of X" is simply "a pointer to X" and using hungarian notation, you can encode the knowledge "this pointer can be incremented" into its name.
and typedef'ing *function declarations? who has families of functions with the same type signatures that they want to point to?
> but have they change something where "pointer to an array[5]" is a meaningful distinction to draw
This has been a meaningful distinction since at least C99.
> "a pointer to an array of X" is simply "a pointer to X"
They aren't actually the same. The former can be decayed into the latter but they aren't actually equivalent and you'll get type mismatches if you try to treat them as the same thing.
> who has families of functions with the same type signatures that they want to point to
An example is callbacks or handlers for responding to interrupt requests. A lot of hardware interface code relies on typedef-ed function decls because very often you are passing user side functions through your interface so that you can stash those function pointers somewhere and invoke them when some event occurs.
> "a pointer to an array of X" is simply "a pointer to X"
I don't believe these two are the same. An "array of X" indeed decays to a "pointer to X". But a "pointer to an array of X" is something else. E.g.
Perhaps the first two are what you mean, though, and this is just a terminology issue.Yes, but this is different:
This is a pointer to an array of 3 ints.And you could pass this to an appropriate function as an argument, to pass the whole array, not just a decayed pointer.
And more generally, I'd group things as unambiguously as possible even in your example:
to make the intent clearerUse typedef?
Granted, the function pointer syntax is forever confusing (to me anyway). The rest is easily tackled by naming things.
Even for function pointers, it’s just one lookup and then you can copy-paste the typedef for any other function pointer types in the project.
Function typedefs make this less confusing by removing awkward parentheses. e.g.
https://github.com/torvalds/linux/blob/0a9b9d17f3a781dea03ba...Reading C Type Declarations: http://unixwiz.net/techtips/reading-cdecl.html
(NOT the author. It simply helped me.)
>Use typedef?
What if you're given somebody else's code and you need to understand it to put a typedef there
The trick is to just assert equal types. Most compilers have extensions that allow you to easily compare type equality (and C23 actually standardizes the `typeof()` operator).
So you basically take your ugly type, put it in a #define and then create a static assertion that matches the type against said ugly type.
Now the compiler will throw a shit fit if the types don't match. Have fun breaking the type up into smaller pieces until you have something legible.
Welp, if it’s that bad then shrug and use a #define
(Yes, this is a joke)
Handy site!
Next I want one to explain some of Rust’s more cryptic pointer gibberish. Usually I just hit “use suggested fix X” until the compiler’s happy.
Rust isn't so bad, is it? The example of `char ((x[3])())[5]` would translate to `[fn() -> [fn() -> u8; 5]; 3]`. It's inherently an ugly type, but I think it's easier to read than the C version.
I can read that.
I don't think it is gibberish. It's code and in order to read that code you need to understand the language, and to understand language you need learning and experience.
Maybe it can be useful for learning, but if you have to use such tool, I suspect you won't understand it anyway - so in a way it is more a gibberish-to-gibberish translator.
I disagree. The conventions for declaring arrays, pointers, and function pointers are all idiosyncratic. In C, the type is always to the left of the variable being declared. Except for arrays, which have part of the declaration to the right. And except for pointers, which need to be affixed to every item if there are multiple declarations. And except for function pointers where you need to wrap the variable name like (*name). Individually I can wrap my head around these exceptions, but putting all of them together, it's just hard to read.
It takes a pretty smart person to do that. Which is pretty confusing.
How can such a smart person not not understand how all things that are possible are not all equally good?
The fact that both the compiler and you can parse that doesn't make it a good way to document or convey meaning or intent.
C is chock full of inconsistencies and ambiguities that are only disambiguated by essentially being a human compiler and maintaining the same parsing state-machine manually in your head to know what any given "(" actually means or does. As a self-proclaimed fluent C linguist, you know this better than most.
All coding involves that of course but all implimentations are not equally unhelpful.
The cpu and some people can read the binary itself. They just need to know the cpu's opcodes, documented right in the datasheet that anyone can read.
Is there a language that's substantially free of gibberish?
Racket Beginning Student [1] language.
[1] https://docs.racket-lang.org/htdp-langs/beginner.html
And its predecessor, Scheme, which is my vote.
Those of Algol/Wirth linage, or influenced by then.
Then again people complain that they are too verbose, and they rather write in hieroglyph friendly languages.
Compare
and or if you wish to replace some keywords with glyphs: And it's always a nice puzzle for the reader to explain why there are three "pointer" in cdecl output and three carets in the ALGOL-like declaration, but only two asterisks in the C declaration.In this case, the C declaration doesn't match the other two. The variable x is a function that returns a pointer to an array of 5 pointers to functions returning char. Indeed, that's what cdecl.org says:
Using the notation you did, that would be: There are only two arrows there now.If you wanted a pointer to a function like this, you would need a third asterisk in the declaration:
Oh, good catch, thank you! But I remember an example with some other tricky C expression/type declarator where the number of actual dereferences differed from the amount of asterisks in the code.
> Using the notation you did, that would be:
Well, it'd be
because it's a function declaration, after all, not a variable.> But I remember an example with some other tricky C expression/type declarator where the number of actual dereferences differed from the amount of asterisks in the code.
Was it this code?
Reading your comment made me think of this. I tried to find the original post for this, but it seems it was deleted. I only found it again through this post: https://zig.news/sobeston/using-zig-and-translate-c-to-under...for declaring function pointer?
Any language with type after name :
Depends on the features you want.
forth will contain as much gibberish as you put into it
Yeah sure, COBOL is great in that regard. Basically reads as English.
nope
Is it just a web wrapper around good old `cdecl` command? Or it does something different/better?
May need some bracket first then English.
Is this largely supplanted by LLMs?
The difference is I trust this website, and wouldn't trust an LLM.
LLMs are mostly correct with regards to this stuff.
cdecl is always correct with regards to this stuff.
I don't know why you'd choose the former.
Because if you don't do things exactly the way cdecl wants you get a Syntax Error
Wouldn't any explanation given, apart from syntax error, be wrong in the case you provide it with an invalid syntax?
I was thinking of the issue of going from English to C. Even when you out in malformed C it's nice to get either a compiler error which explains the problem, or even better an LLM that can suggest how to fix it.
Probably.
Output for the example I got on opening the website:
cdecl.org: declare x as function returning pointer to array 5 of pointer to function returning charChatGPT: x is a function that, when called, gives us access to 5 functions that each return a character. (TL;DR, it gave a full explanation too)
Like mentioned before the error rate of LLMs is probably much higher on complex expressions.
Why is it that the first thing I try tends to uncover shortcomings?
typedef uint64_t qbb_t __attribute__((vector_size(sizeof(uint64_t) * 4)))
Syntax error
OK, its an extension, meh.
Looking at the project issues, it seems it supports only 1989-era C, and some Apple stuff, before you even get to attributes.
This failed because it doesn't do math: int *x[10/2]
[dead]