September 08, 2022

On Thursday, 8 September 2022 at 14:50:01 UTC, Atila Neves wrote:

> >

One could be what the top comment on that video says: "It wasn't the language itself, it was the people, and how they chose to use the language, and how they chose to show everyone how smart they were by using every possible feature of the language to its largest extent possible".

That same person claims that "but the worst ANSI C I have encountered was easier to fix than the worst C++ I have encountered". Not in my experience.

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. 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).

Worse, the complex C++ features are often recommended by the guidebooks, as opposed to macro trickery which is officially advised against.

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.

>

And that's part of the problem: we're all shaped by our own past experiences, and that's easy to see when looking at what different programmers prioritise. I think it's all a reaction to being burned before and avoiding that, but we've all been burned in very, very different ways.

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. 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.

Related to getting burned by C++: Teoh made an interesting rant about that before.

September 08, 2022
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
September 09, 2022

On Thursday, 8 September 2022 at 22:26:12 UTC, H. S. Teoh wrote:

>

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.

You got me to doubt my assessment ;).

> >

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.

I disagree somewhat, because it could be taught that one should avoid or be extra conservative with SFINAE because Koening lookup, and many other things, tend to mess it up. That's what I meant with bad teaching: encouraging to use C++ features which bite back like that.

>

Only reach for the ultimate weapon (did I hear, string mixin? ;-)) when no other recourse suffices.

In my opinion string mixins should not always be the last resort. Sure they have the downside that they are essentially macros that can do anything. Walter did wisely requiring the mixin keyword when using them, so they stand out clearly and are too ugly to be tempting.

But mixins have the upside that they are much simpler to understand than template and trait trickery. That's why a string mixins are IMO sometimes better than clever templates.

September 09, 2022
On Fri, Sep 09, 2022 at 09:36:16AM +0000, Dukc via Digitalmars-d wrote:
> On Thursday, 8 September 2022 at 22:26:12 UTC, H. S. Teoh wrote:
[...]
> > 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.
> 
> I disagree somewhat, because it could be taught that one should avoid or be extra conservative with SFINAE because Koening lookup, and many other things, tend to mess it up. That's what I meant with bad teaching: encouraging to use C++ features which bite back like that.

That's the problem with C++: its features bite back.  What business does a feature that bites back have in a programming language??  Regardless of how the language is taught, such features shouldn't be in the language in the first place.  To shamelessly quote myself:

	A programming language should be a toolbox for the programmer to
	draw upon, not a minefield of dangerous explosives that you have
	to very carefully avoid touching in the wrong way. -- Me.


> > Only reach for the ultimate weapon (did I hear, string mixin? ;-))
> > when no other recourse suffices.
> 
> In my opinion string mixins should not always be the last resort. Sure they have the downside that they are essentially macros that can do anything.  Walter did wisely requiring the `mixin` keyword when using them, so they stand out clearly and are too ugly to be tempting.
> 
> But mixins have the upside that they are much simpler to understand than template and trait trickery. That's why a string mixins are IMO sometimes better than clever templates.

It depends on the use case.  Templates are much nicer than mixins in certain cases, but in other cases it may be the other way round.

Hence my rule-of-thumb for D: write the code in the simplest way possible; when that fails, reach for the next simplest tool.

In fact, in my own D projects I have resorted to external codegen in some cases, instead of adding yet another layer of mixins/templates to the code.  Sometimes, it's not worth the extra complexity and maintenance burden just to have the bragging rights that you did it all within the confines of the language; sometimes, the more sensible thing to do is just to write a utility program that generates the desired D code, and compile that into your main executable.

(Of course, this does assume a reasonably sane build system... if you're stuck with Certain Hobbled Build Tools *cough*notnamingnameshere*cough*, then over-complex templates and unreadable mixins may be your only recourse. :-P)


T

-- 
Winners never quit, quitters never win. But those who never quit AND never win are idiots.
September 10, 2022
On Friday, 9 September 2022 at 15:58:12 UTC, H. S. Teoh wrote:
> On Fri, Sep 09, 2022 at 09:36:16AM +0000,
...
> In fact, in my own D projects I have resorted to external codegen in some cases, instead of adding yet another layer of mixins/templates to the code.  Sometimes, it's not worth the extra complexity and maintenance burden just to have the bragging rights that you did it all within the confines of the language; sometimes, the more sensible thing to do is just to write a utility program that generates the desired D code, and compile that into your main executable.
>
> (Of course, this does assume a reasonably sane build system... if you're stuck with Certain Hobbled Build Tools *cough*notnamingnameshere*cough*, then over-complex templates and unreadable mixins may be your only recourse. :-P)
>
>
> T

In my experience this can lead to, hard to debug code, since the error is in the generated code. Although of course debugging template heavy or mixin filled code is also not a picnic. How do you avoid this kind of  problem? For instance if you use, say, vide.d templates, which are an example of heavy usage of compile time features, and make a mistake in a template you often get a somewhat meaningful error message relating to the template file. This would probably be harder  with an external tool (since it would have to be able error check arbitrary d).
September 10, 2022
On Sat, Sep 10, 2022 at 05:57:53AM +0000, JG via Digitalmars-d wrote:
> On Friday, 9 September 2022 at 15:58:12 UTC, H. S. Teoh wrote:
> > On Fri, Sep 09, 2022 at 09:36:16AM +0000,
> ...
> > In fact, in my own D projects I have resorted to external codegen in some cases, instead of adding yet another layer of mixins/templates to the code.  Sometimes, it's not worth the extra complexity and maintenance burden just to have the bragging rights that you did it all within the confines of the language; sometimes, the more sensible thing to do is just to write a utility program that generates the desired D code, and compile that into your main executable.
[...]
> In my experience this can lead to, hard to debug code, since the error is in the generated code.

In fact, this is precisely the advantage of separately generating the code: you can examine the generated .d file for yourself and see precisely what was generated.


> Although of course debugging template heavy or mixin filled code is also not a picnic. How do you avoid this kind of  problem?

Template-heavy / mixin-filled code is a nightmare to debug, if they were not written to be debuggable. Because you cannot see what's the final code that the compiler compiles.  Whereas generating a .d file as a separate compilation step lets you examine the file yourself and find where the bug is.


> For instance if you use, say, vide.d templates, which are an example of heavy usage of compile time features, and make a mistake in a template you often get a somewhat meaningful error message relating to the template file.

That's because the diet templates' code was written to generate these nice error messages. When you run into a case the authors didn't anticipate, good like trying to debug the heavily-nested diet code...

Of course, an error in a separately-generated .d file may not be trivial to debug either, but presumably you also wrote the helper utility that did it, so by seeing the error in the generated .d file, it would guide you to look at the section of the helper utility responsible for generating that bit of code, and you can go from there.


> This would probably be harder  with an external tool (since it would have to be able error check arbitrary d).

This is another disadvantage of using templates/mixins: external tools have to be able to parse templates and magically infer what exactly is the mixed-in string, in order to help you.  An externally-generated .d file is just regular D code, you just use standard code utilities on it and you're ready to go.


T

-- 
They say that "guns don't kill people, people kill people." Well I think the gun helps. If you just stood there and yelled BANG, I don't think you'd kill too many people. -- Eddie Izzard, Dressed to Kill
September 10, 2022
On Saturday, 10 September 2022 at 13:58:51 UTC, H. S. Teoh wrote:
> On Sat, Sep 10, 2022 at 05:57:53AM +0000, JG via Digitalmars-d wrote:
>> > [...]
> [...]
>> [...]
>
> In fact, this is precisely the advantage of separately generating the code: you can examine the generated .d file for yourself and see precisely what was generated.
>
>
>> [...]
>
> Template-heavy / mixin-filled code is a nightmare to debug, if they were not written to be debuggable. Because you cannot see what's the final code that the compiler compiles.  Whereas generating a .d file as a separate compilation step lets you examine the file yourself and find where the bug is.
>
>
>> [...]
>
> That's because the diet templates' code was written to generate these nice error messages. When you run into a case the authors didn't anticipate, good like trying to debug the heavily-nested diet code...
>
> Of course, an error in a separately-generated .d file may not be trivial to debug either, but presumably you also wrote the helper utility that did it, so by seeing the error in the generated .d file, it would guide you to look at the section of the helper utility responsible for generating that bit of code, and you can go from there.
>
>
>> [...]
>
> This is another disadvantage of using templates/mixins: external tools have to be able to parse templates and magically infer what exactly is the mixed-in string, in order to help you.  An externally-generated .d file is just regular D code, you just use standard code utilities on it and you're ready to go.
>
>
> T

I agree in part with what you say, perhaps I will try it again sometime.
My last attempt was a few years ago being fed up writing go and not having templates. So I wrote a program that transformed "my templated go" into go.
For small things it was fine but when I tried to use it more seriously I
found trying to bug fix it painful. (In case someone feels the need to inform me that go has generics now - I am aware of that. I tried them I think they are not too great).
September 13, 2022
On Sat, Sep 10, 2022 at 05:05:18PM +0000, JG via Digitalmars-d wrote:
> On Saturday, 10 September 2022 at 13:58:51 UTC, H. S. Teoh wrote:
[...]
> > This is another disadvantage of using templates/mixins: external tools have to be able to parse templates and magically infer what exactly is the mixed-in string, in order to help you.  An externally-generated .d file is just regular D code, you just use standard code utilities on it and you're ready to go.
[...]
> I agree in part with what you say, perhaps I will try it again sometime.  My last attempt was a few years ago being fed up writing go and not having templates. So I wrote a program that transformed "my templated go" into go.  For small things it was fine but when I tried to use it more seriously I found trying to bug fix it painful.
[...]

If I'm not mistaken, sounds like what you had wasn't so much a code generator as a Go preprocessor, of sorts.  I usually don't bother with codegen in this case, because D's templates work wonderfully.  The kind of codegen that I'd need an external codegen to do, is usually when there's some external data source or API driving the codegen (which in theory I can do purely in D with CTFE and reflection, but at considerable compile-time cost).

For example, automatically generating D APIs for GLSL shader uniform / vertex attribute bindings by inspecting GLSL code.  The generated D code allow me to use nice object.field syntax to set these bindings, eliminating a whole bunch of fiddly boilerplate that would be very error-prone to write manually.

Or processing input 3D data files and auto-generating code for uploading vertex data converted to the appropriate format to the GPU, and exporting a nice API for manipulating these objects. Especially in this case, although in theory it's possible to load the data files with string imports and process them in CTFE, the result would be compilation unacceptably slow.  By processing them separately, I only need to generate the target .d file once, and it can be compiled as a normal source file, and only need to be regenerated when something changes in the input data files.


T

-- 
You are only young once, but you can stay immature indefinitely. -- azephrahel
June 23, 2023
23.06.2023 13:17, sonal13 пишет:
> 
> The fact that C++ is a superset of C also means that it can be used to extend C programs. This is often done by adding new features or functionality to the program. For example, a C++ program could be used to add object-oriented programming features to a C program.
> 

I wouldn't say that c++ is a superset of C. There is a difference between these similar languages.

1 2 3
Next ›   Last »