H. S. Teoh
| On Thu, Sep 08, 2022 at 09:15:27PM +0000, Dukc via Digitalmars-d wrote: [...]
> Going crazy with macros will certainly make C as hard to read as any C++. But I think there may be a point in this otherwise. In C one pretty much only has to avoid being clever with the preprocessor, and it's hard to totally mess up the readability.
Are you ... *sure*? :-P
Haha, wait till you see some of the C code I've had to deal with. Somebody, in a glorious fit of NIH, had decided that it was a good idea to reinvent C++'s OO -- except partially, inconsistently, and without the syntax niceties and compiler guarantees -- by writing function pointers everywhere. And I mean, literally *everywhere*: open up a page of code the code, and almost every line involves at least 1 call to a function pointer. Most lines contain at least 2-3, some really bad ones have 6 or more. Basically, it's trying to imitate C++ polymorphic code except in C. But the problem is, since this is all hand-made and not supported nor enforced by the compiler, the correspondence between function pointers and logical virtual method calls is not 1-to-1... meaning that sometimes you *think* a certain funcptr call would go somewhere, but it goes somewhere else instead, because somebody along the line decided to "override" it (unofficially) in order to, I don't know, fix a bug or something.
And being all hand-spun, it's anybody's guess where these funcptrs are initialized. You don't have syntactic ctors and class definitions that you can look up to find out; these are all squirrelled away in obscure parts of the code that, themselves, are chockful of funcptrs leading who knows where. Suspect a bug on some particular line of code? Good luck figuring out where that funcptr actually leads... Well, you say, that's easy, just find out where it's initialized. OK, so you try to figure out where it's initialized (hint: it's not anywhere you might imagine, you have to search long and hard for it). After you locate the code that initializes it (by which time you're already tearing out your hair in frustration), you see that it's also riddled with funcptr calls head-to-toe, and exactly which value it uses to initialize the original funcptr is dependent on a whole bunch of other funcptrs that leads who knows where. So, to figure out where 1 funcptr leads to, you have to decipher 15 others. And each of them, in turn, requires deciphering where another 15 lead to (because their respective initialization functions, you see, are written in exactly the same way).
So you can stare at the original function until the cows come home, and you'd have no idea what it actually does. Forget about reading the code; the only way to figure this out is to use a debugger and breakpoint it, then inspect where the funcptrs actually point to at runtime. Trying to decipher the initialization code is like getting caught in a shark loan: you start out with a debt of 1 funcptr, and you end up owing another 15, and for each of the 15, you owe yet another 15. Compound funcptr interest FTW.
Yep, definitely hard to totally mess up readability in C. :-P
> But in C++ there are so much complex features that the code can have much worse readability issues than those caused by typical C problems (poor naming, no comments, lots of global state, oversized functions).
These are by far not the only problems with C. :-D (See above. :-P)
[...]
> Maybe the conclusion is that C++ is virtually always better than C in itself, but it's use is teached so badly that it often ends up worse.
Um, no. SFINAE + Koenig lookup. That combo alone will give you migraines for weeks, if not months. No amount of good/bad teaching will negate the fact that this combo is the stuff of nightmares. Add to it mix all the other "niceties" of C++, and ... yeah. Not going there. I think I'm suffering from C++ PTSD.
At least in C, once you nailed down that darned funcptr, you *know* it isn't gonna cheat on you and go on a tryst with a completely unrelated module that you hadn't suspected was surreptitiously #include'd where the same identifier was gratuitously reused just to spice up your life. Identifier hijacking FTW!
[...]
> Since D is a fairly complex language, I think we should talk more about when we should avoid the most complex features, such as traits.
One of the things I love about D is: the easier it is to write something and the simpler it looks, the higher the chances are that it's actually correct. (Contrast this with C++, where the simplest way to write something is almost certainly the wrong way; the right way involves arcane incantations of black magic that not even seasoned C++ coders have come to an agreement of which way is actually correct. And the resulting code reads very much like an undecipherable arcane inscription reverse-encrypted in Klingon.)
So I'd say the rule of thumb in D is, write it in the simplest way possible to achieve what you want. If that fails, then consider the next more complex thing up the ladder of complexity. Only reach for the ultimate weapon (did I hear, string mixin? ;-)) when no other recourse suffices.
> I suspect typical D programmers are not burned by overengineered language features as much as C programmers. Don't get me wrong, traits are great, but they do have a complexity cost in readability and error message quality. Sometimes a less perfect but simpler feature is the better choice even in D.
[...]
Agreed.
T
--
"A man's wife has more power over him than the state has." -- Ralph Emerson
|