
D as a C Replacement - signa11
https://theartofmachinery.com/2019/04/05/d_as_c_replacement.html
======
AstroJetson
I've preferred C to C++ and D to C++ for a long time. To me when I read C++
code it's like the modem disconnected in the middle of the editing session
with all the : and << flying around.

For things that don't need classes (and there is a lot of code) I stick to C.
But if I need the OOP benifits then I go with D. As a bonus, the C programmers
and JavaScript programmers that I work with can read the D code and get most
of it on the first try.

If you haven't used D, spend 30 mins and write a four function calculator.
Then take the remaining time adding some kind of internal function line sin,
cos, etc. You'll be suprised at how fast you can pick it up.

Granted, the above is a toy exercise, it's not writing an enterprise solution.
But it's a little more than the standard "Hello Sailor, new in town" program
that people start off with.

~~~
nextos
I also prefer C and D to C++. But used wisely, C++ can be a really nice
language after all changes that started with C++11.

The key is to realize C++ is not a language, but a federation of languages.
You need to cherry pick a subset you what you want to use and stay
disciplined. Otherwise, it is a mess.

~~~
eindiran
> The key is to realize C++ is not a language, but a federation of languages.
> You need to cherry pick a subset you what you want to use and stay
> disciplined.

The issue is that no matter how disciplined you are, others will be using a
different subset of the features available and you end up using different
"languages", even when everyone on your team is ostensibly writing "C++".

~~~
nextos
Aren't all powerful languages poised to have this problem?

You can write generic imperative C++ by using lots of templates, OO code that
looks like Java, low level imperative code similar to C, and even functional
code.

I can personally say the same thing about Haskell or Common Lisp. Perhaps it's
not that extreme, but there are many ways to solve problems in those
languages. Even some funny jokes about Haskell exploit this:

[https://www.cs.utexas.edu/~cannata/cs345/Class%20Notes/10%20...](https://www.cs.utexas.edu/~cannata/cs345/Class%20Notes/10%20Haskell%20Programmer%20Evolution.html)

~~~
flukus
> Aren't all powerful languages poised to have this problem?

Probably. How many of these powerful languages started out that way and
received widespread adoption though? C++ was basically C with classes when it
become big. C# and Java are complex beasts now but both gained popularity when
they were much simpler.

Programming languages need to be comprehensible to your average programmer and
not just a few elites.

~~~
pjmlp
Programming languages for the average programmer end up with libraries full of
boilerplate and code generators to work around lack of features.

------
jordigh
D has been a real pleasure for me. I did the 2017 AoC in it:

[http://jordi.inversethought.com/blog/advent-
of-d/](http://jordi.inversethought.com/blog/advent-of-d/)

The richness and flexibility of compile-time features almost make it feel like
a lisp, with the slight downgrade of sometimes working on strings of source
code instead of sexps. Regardless, like the article says, D's metaprogramming
alone is worth the entry price of reading a few books or tutorials about the
language.

~~~
MrRadar
The way you characterize D's metaprogrammability is exactly how I would put it
too. It's hard to describe but D just makes metaprogramming so fun and easy
compared to anything besides Lisp and I find it sorely missing when working
with most other languages.

------
buserror
I never understood the use of D, or any of the "C replacements" languages
trends. C does work pretty well, but it's like a sharp tool. Use with a lot of
care with your remaining fingers.

There are a LOT of things C is blamed for, and languages try to introduce
complicated syntaxes and abstraction to prevent them from happening, however,
most of these other languages are ALSO open to the same security issues (or
sometime, more), just on a different level. Perhaps they look a lot safer
because they haven't had the same level of scrutiny as good old C. In the
meantime they introduce a 'duh/WTF' effect when you try to bring someone else
in on the codebase.

C with good static analyser coverage (cppcheck, clang 'scan-build', smatch[0])
and reasonable discipline is pretty easy to maintain, VERY easy to debug,
extraordinarily low footprint and very fast, so taking the time and having a
BIT of discipline to harness that power is well worth it.

As the article mentions, I'm one of the guys who did 20 years of C++ before
'reverting back' to C, and I'd LOVE a few of the things I've had to give up --
however, a new language is not really something I need.

[0]: [http://smatch.sourceforge.net](http://smatch.sourceforge.net)

~~~
AgentME
>most of these other languages are ALSO open to the same security issues

How often do programs in non-C/C++ languages get remote code execution
vulnerabilities? I'm sure the CVE numbers will overwhelmingly show that most
RCE-capable vulnerabilities are in C/C++.

Sure, it occasionally happens in other languages, but practically only when
someone does something like misuse one of a specific set of APIs that allow
code loading. In C/C++, any code that slightly fucks up writing to an array or
passes a pointer to previously-freed memory around can cause a RCE
vulnerability that lets an attacker literally run code on your system and take
it over. Inspecting a C/C++ codebase made by a junior person for
vulnerabilities is a total nightmare. Inspecting a codebase in any other
language for RCE vulnerabilities generally means you grep for a couple APIs
and look at how they get used.

~~~
buserror
I don't believe that, you mention this because you put 100% trust _on the
runtime_. When I see syntax like: var array=something[4...$] I don't see
security there, all I see is someone pushing the problem further under the
carpet, into the runtime.

So Sure, RIGHT NOW the runtime might be safe from scrutiny, but is it because
its SAFE or just not as such a high profile, just yet?

On the other hand I DO agree that 'junior' programming will always be a
problem, regardless of the matter, and pretty much, regardless of the
language.

~~~
klez
The difference is that if the bug is in the runtime you have to fix one place
and everyone downstream gets that. If you solve one such bug in curl, you
solved it just in curl.

EDIT: I thought about it a bit more and realized it cuts both ways. If you
have an RCE in a standard library a lot of programs get an RCE for free while
it's not patched. So yeah, a bit of a mixed bag.

~~~
ppseafield
There's also the difference that, in a GC'd language, you don't have both the
language authors and the language users both competing to put these kinds of
bugs in their software. :)

~~~
tomatocracy
True, but when you want to interface between code written in two different
GC'd languages then you can get similar problems if you're not careful. As the
number and use of these various next generation languages proliferates, I
suspect we might start to see more of that kind of thing appearing over time.

------
xedrac
I am admittedly a D novice. I would choose it over C in a heartbeat for most
things. However, with how good Rust has become, it's hard for me consider D
for anything. Rust is a fantastic C/C++ replacement IMO, and I'm hugely
invested in C++.

~~~
atemerev
Well, if constantly fighting borrow checker is your thing...

~~~
hollerith
Some people bristle at any restriction on their freedom of action (which
psychologists sometimes call "trait reactivity") whereas some people are OK
with a restriction if there are practical benefits.

~~~
cinnamon_bun
I would call this phenomenon "cowboy development". Why would you fight borrow
checker when you can employ your users to flush out crashes and undefined
behavior?

~~~
pjmlp
Because one is foolish to try to implement GUIs in Rust, which most GC
languages are much better suited for.

------
lacampbell
D is one of those language that while it's technically very good, it's small
eco system makes me look over it. I'm quite conservative about what libraries
I rely on, and with these smaller languages 90% of the libraries you want are
someones hobby.

The betterC scenario has intrigued me before though, it seemed like a win all
around since you'd be using C libraries anyway. I'd love to hear others
experiences and anecdotes with this.

~~~
vaylian
> it's small eco system makes me look over it.

You have relatively clean access to all C libraries. But I do not know if that
is relevant enough in your area of work/play.

And the standard library is also pretty nice. Sure, it's nowhere near Python's
Batteries-Included package, but it can get you quite far.

~~~
flukus
It looks like you still have to write interfaces for c libraries
([https://dlang.org/spec/interfaceToC.html](https://dlang.org/spec/interfaceToC.html))
and that it can't use header files directly like zig. This is fine for interop
but it's not great as a drop in replacement, there is still some manual work
and likely many edge cases around macros in the c libraries.

I'm yet to put zig through it's paces but you can include header files
directly, theoretically that makes it a much better drop in replacement.

~~~
nicwilson
You can write interfaces and for many projects that works fine. There are
tools like [https://github.com/jacob-carlborg/dstep](https://github.com/jacob-
carlborg/dstep) to convert headers and tools like
[https://github.com/atilaneves/dpp](https://github.com/atilaneves/dpp) to
"preprocess" the D source to include and translate headers

------
quicknir
I think the author blows his credibility very early with this comment:

> As I said, it’s a superficial example, but I think it shows a general
> difference in philosophy between C++ and D. (If I wanted to make the
> difference even clearer, I’d use an example that needed iomanip in C++.)

iostreams are not in C++ because C++'s philosophy is actually that iostreams
are great. Everyone knows they suck. They are there because without variadics,
there isn't a good way to do _type safe_ text output in the style of printf.
And I mean, not even _runtime_ type safe. Chaining of some kind is the obvious
way to simulate variadics when you don't have variadics. And at the time, they
thought it was better to get something type safe into the standard library,
then gate it behind variadics which could (did) take a long time. Voila,
iostreams.

C++ is quite literally in the process of standardizing a library that will
bring type safe printf (a la D) to C++. The same way that 8 years ago, C++
finally managed to standardize variadics after quite a lot of effort.

The disadvantage of being an old language is that it can be hard to stay
caught up with features. The advantage is that you get a huge base of existing
developers, knowledge, libraries, etc.

Bloggers love to make things about big picture philosophy because it makes for
better blurbs but many things in reality are just engineering decisions. D had
the luxury of creating metaprogramming syntax from scratch after one if its
creators was one of the main people to discover the power of "accidental" TMP
in C++. C++ is still trying to bend accidental TMP into something more
bearable to use without breaking everything. The differences here are more
practical than philosophical.

~~~
munificent
_> They are there because without variadics, there isn't a good way to do type
safe text output in the style of printf._

I think an equally important point is that IO streams in C++ are extensible.
You can make your own user defined types work with them. There's no way to add
"printf() support" to your own type in C.

~~~
flukus
> I think an equally important point is that IO streams in C++ are extensible.

Not sure how portable it is but you can define conversions for your own types:
[http://www.gnu.org/software/libc/manual/html_node/Customizin...](http://www.gnu.org/software/libc/manual/html_node/Customizing-
Printf.html) . The format and format_arg attributes give you compile time
support: [https://gcc.gnu.org/onlinedocs/gcc/Common-Function-
Attribute...](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-
Attributes.html#Common-Function-Attributes) .

~~~
pjmlp
It is just GCC C.

~~~
flukus
It's part of libc, so not GCC specific, it's basically native on linux and
usable just about anywhere you'd want printf functionality. If the bloat (like
this feature) of gnu libc are too much then I doubt the c++/d/rust equivalents
will be an acceptable option. Likewise if gcc and clang (which supports
printf) aren't possible then I very much doubt the platform is a compiler
target for modern c++/d/rust.

------
dataking
Scope guards seem like a really good idea. Has there been any effort to write
a C to -betterC transpiler just like there has for Rust (e.g. C2Rust [0],
Corrode [1])?

[0]
[https://www.github.com/immunant/c2rust](https://www.github.com/immunant/c2rust)

[1]
[https://www.github.com/jameysharp/corrode](https://www.github.com/jameysharp/corrode)

~~~
mhh__
Yes. D's design is based around the idea that semantically valid C will
compile in D (apart from crud like function pointer syntax etc.) but there are
tools to convert C to D (As used in the dmd compiler _backend_ (which dates
back to the 80s I believe, but is now in a tasty form of spaghetti D)

~~~
earenndil
> D's design is based around the idea that semantically valid C will compile
> in D

AFAIK it's that code which compiles in both c and d should have the same
behaviour; not that c code should necessarily compile in d. Examples of c that
don't work in d are trivial.

> there are tools to convert C to D (As used in the dmd compiler backend
> (which dates back to the 80s I believe, but is now in a tasty form of
> spaghetti D)

Dmd's backend was ported by hand. What tools are those? I know there are tools
to convert header files, but I don't know of any that actually convert code.

~~~
nicwilson
> Dmd's backend was ported by hand.

Not quite, the c++ source was massaged to be free of weird corner cases of the
grammar and preprocessor (e.g. enforcing no spaces between '#' and the
preprocessor keyword) and then a tool called magicport was used to convert the
frontend in one shot. The backend was mostly converted by "hand" (i.e. grep
after some more massaging).

There are several tools to translate headers though dstep[1] and dpp[2] come
to mind

[1]: [https://github.com/jacob-carlborg/dstep](https://github.com/jacob-
carlborg/dstep) [2]:
[https://github.com/atilaneves/dpp](https://github.com/atilaneves/dpp)

~~~
WalterBright
Magicport was used to convert the front end. The back end was done by hand,
one file at a time (and passing the test suite after each file).

Most of the conversion was "replace -> with ." and then fixing whatever the
compiler complained about.

------
asveikau
The linked article on C++ method pointers loses some relevance now that C++11
and newer have lambdas and std::function. Still an interesting rabbit hole.

I agree that the Digital Mars implementation looks like the sanest.

------
jfries
Cool! -betterC looks well worth exploring. Also, pure and safe are fantastic
features, I just wish the default was the other way around, so you need to
specify impure and unsafe.

~~~
mhh__
If the language were being done today I suspect that could've ended up being
the case, but keep in mind that (given that idiomatic D encourages use of
templates) these will be inferred automatically upon instantiation of a given
template

------
SantiagoElf
To be honest, I was always fascinated by the D language. I am glad it is still
alive and kicking. Evolving too :)

------
rubber_duck
Whats the story with D portability ? Can it compile to iOS Android and WASM
without a lot of issues ?

C and C++ can compile to anything - thats a major use case for me

~~~
earenndil
D does technically run on all three of those platforms (ios, android, wasm).
Of the three, android is probably the most mature. Wasm is not very usable,
because wasm itself doesn't yet have a garbage collector, so the d runtime is
not ported. For iOS, there is a fork of GDC (GCC-based compiler) which is
supposed to run, but there isn't very much interest in it. If you put in the
work, you could likely get all three to work, but there's nothing OOTB yet.

------
alganet
Very dool. I dompletely agree.

------
anfilt
D is not a C replacement. It's more targeted at C++. Honestly, I prefer D to
C++, but still prefer C to D. I would say the biggest turn off to C is
generally it's very small STD library. While there are a lot C libraries that
exist to achieve certain things it's not part of STD C. It's not part of the
"out of box" experience.

It also depends on what your programing if you looking at system level stuff
currently the two languages I would look at are C and Rust. Heck even for some
system stuff your still required to use assembly.

Anything else choices are more flexible and productivity might matter more.

Honestly, despite C's flaws very few languages occupy it's niche and
flexibility. Like you have to be doing something right to some degree to last
almost 50 years. I would say a lot this comes down to C's simplicity yet still
having a lot of power as a language.

------
DeathArrow
I don't see the need for a C replacement. We have 2 use cases:

a) you absolutely need performance, so a GC is out of question b) you don't
need absolute performance

In case a) I don't think there are better solutions than C In case b) you
wouldn't use C, to begin with. C#, Go, Java are ok.

~~~
auvrw
additionally, there's (c) kernel and/or embedded.

in particular, and i've read that writing kernels isn't feasible in Go. it
appears not to be an issue of performance. something about the runtime
prevents writing kernels altogether.

like the linked article says

[https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
repl...](https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
replacement.html)

rust has a lot of features (generics, etc.) aside and apart from the memory
management strategy.

i'm also unaware of a procedural language that has the feature stability of C
or Go as well as the ability to write kernels or embedded code with the memory
safety of rust.

currently wondering how easy it'd be to tack rust's memory model onto another
language, alef in particular.

------
koff3
I like D a lot but find it's not a good fit for many things. C++, Java and
Python have great tools and libraries to rely on. Not so much with D's small
community. Writing wrappers and interop always feels like a hassle, so it's
easier to stick with just one language.

First time I used D was before the version 2, back when there was the Tango
library. D seems to have improved quite a bit since then, but it's still in
the same position as before.

------
quelsolaar
I disagree about being better then C. Compile time asserts can be created
using #error in C, and having something happen out of order at the end of a
scope is a terrible idea. One of the main strengths of C is that everything is
explicit and nothing happens without you saying so.

~~~
ben-schaaf
> Compile time asserts can be created using #error

Really not the same thing. Compile time asserts allow you to check any compile
time value. For instance say you were writing OpenGL code and your struct
needs to have a size that is a multiple of 16 bytes. In D that's easy:

    
    
        static assert(sizeof(Data) % 16 == 0);

~~~
nn3
build time asserts can be defined in C macros with some tricks. The error
messages are slightly misleading, but when people look at the source code it
is usually clear what is happening.

#define ASSERT_BUILD(x) \ do { char array[(x) ? 1 : -1]; (void)array; }
while(0)

static char foo[10]; ASSERT_BUILD(sizeof(foo) >= 10);

~~~
ben-schaaf
That's a great trick and all, but it falls disappointingly short of D's
`static assert`: There's no guarantee it'll ever run. The trick assumes that
the C compiler will evaluate the expression at compile time, but there's every
chance that it just won't and will silently fail instead.

    
    
        char * i = malloc(1);
        ASSERT_BUILD(i);
    

That just silently compiles and runs without triggering any failure. Whats
worse is you can never be sure whether your assert is passing or the compiler
decided not to run it.

~~~
nudq
That compiles because with gcc extensions or sufficiently recent standards,
variable size arrays are now a thing. The compiler doesn't insist on a static
array size. What about something like this:

    
    
        #define ASSERT_BUILD(x) extern int STATIC_ASSERT_FAILED[(x)?1:-1]

------
dnautics
The post this is referring to claimed as its major complaints about rust its
1) lack of a spec and 2) its increasing number of features, neither of which
are addressed by the author.

------
xvilka
Zig[1] looks like more interesting and modern alternative, albeit immature
one.

[1] [https://ziglang.org](https://ziglang.org)

