(Note, by "used" here I mean utilizing most of the language, and the full ecosysstem of libraries, or at least most of it; not a mode that is supported in theory but useless in practice.)
Yes, if you're willing to spent some effort on it.
Somewhat simpler you can just avoid most GC allocations so you don't run into any performance issues with the GC.
http://dlang.org/blog/the-gc-series/
Mind to share why a GC seems to be unaccceptable for you?
It's not that it's unacceptable in general (although there are certainly scenarios - real-time, embedded etc - where it is). It's that a language that effectively mandates GC cannot be a true C++ replacement. So I can't in good conscience accept a "better C++" or "what C++ should have been" label.
This isn't to say that D is not a great language. It is, but I see it more as competing against Go (which is also not "a better C"), and to some extent Java, C#, Kotlin, and Swift.
The only true contender to the "better C++" label that I know of is Rust.
You can already write C++ in D, all the features are there. On top of this, you get features that C++ should have had years ago for free: Static Reflection, UFCS (Less boilerplate), Meta-Programming that isn't a hack and lots of static guarantees (Purity, GC usage etc).
The standard library could have been implemented completely without the GC, but the GC makes programming simpler so it's not worth it. Practically, this may be a problem, but it's a little unfair to compare to use this against D (only the language) given that Phobos is not standardized in the same way that the C++ Standard Library.
In a very specific way (May or may not be good in your opinion), I think D is closer to C++ than Rust is: D maintains the basic idea of C++ e.g. Syntax + semantics are still C based, the type system is less complex than that of Rust.
However, Rust has better static safety guarantees than D. D's system is still being developed, although does already offer much more than C++.
The problem is the stop the world pause. Imagine the following scenario: Thread 1 is a realtime thread and allocates memory up front and even uses @nogc for the rest of the code. Thread 2 continuously allocates memory. Thread 2 starts the garbage collector and must stop Thread 1 even though it doesn't allocate anything. Basically the only solution to the stop the world pause with D is to run two instances of your program and then use IPC even though the language provides a way to disable the gc. D's solution is to generate less garbage but it doesn't solve the real problem with current garbage collector implementations.
> Basically the only solution to the stop the world pause with D is to run two instances of your program and then use IPC even though the language provides a way to disable the gc.
There is a solution to your stated problem that is to deregister Thread 2 to the runtime. No registration -> no stop-the-world for this thread.
Thread 2 will not be able to "own" GC things or allocate with the GC but will perfectly be able to use them. I did this in a video game for the audio thread.
Writing in languages with a GC is quite cumbersome.
Unless you are writing a proof of concept without a care in the world for your code you still need to reason about memory just the same as in a language without a GC. Only problem is that you constantly have to fight the GC and you don't get any help from the language. And at the same time you loose valuable constructs such as RAII - for no good reason.
Similar to how static language are gaining I think that we are going to realize that garbage collection has been a huge mistake. It is such a relief that we now got Rust, and the lost opportunity that is Go is nothing but tragic.
In the same way as dynamic languages simplifies programming for the vast majority of use cases. Or not, it's just short sighted and introduces unnecessary complexity.
When it becomes a problem it is already too late, because you can't just change your mind - "oh, I guess I'll think about memory now". Because that is something you should always do regardless if a GC is present or not. And if you haven't, well, prepare to spend the remaining liftetime of that project battling with it. And if you did, why bother with the GC in the first place?
It is not difficult, and forces you to write better software.
I was talking about GC in general terms, not specifically about D. That RAII example seems a bit convoluted though. Maybe D is a great language even when ignoring the GC, but often enough using a language in a way that isn't officially meant (and I bet most do make use of and expect the GC to be available?) is usually a bad idea.
D's GC is designed in such a way that's it's very easy to avoid. It only operates when allocating, if you want it to not collect from a thread just disconnect that thread from the runtime.
Using a GC does not constitute not worrying about memory!
There is no official way to use D; the only real problem with not using the GC now is safety (Exceptions pass pointers and can therefore leak across library boundaries).
That negates the whole point of templates and constexpr -- they're purely functional subsets and explicitly designed to be so.
(You can argue that purely functional code is useless in real life and that C++ shouldn't strive to be purely functional, but that's another argument.)
There is no macro needed here. You just write the code, then execute it at compile time. This is designed properly, it's not any where near as hacky as C++'s constexpr
(defmacro html (string)
(some-html-parser-library:parse string))
Or you could use a function and a compiler macro to make it more of an optimization -- if the argument is known at compile-time just parse it then, otherwise wait til runtime:
(defun html (string)
(some-html-parser-library:parse string))
(define-compiler-macro html (&whole form string)
(if (constantp string)
`',(some-html-parser-library:parse string)
form))
Actual example, using `string-upcase` to stand in for an HTML parsing library:
Yes. We parse JSON at compile-time with std.json (in D standard library). It probably wasn't even designed with that goal. From a D point of view, compile-time parsers are almost boring since anyone can write them.