Considering that one of the greatest C++ programmers of all time (Andrei) is now a D person, that's proof enough.
But for some reason, this sort of C++ shenanigan is intensely satisfying to me.
I guess it's a narrow complexity fetish.
(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.)
Mind to share why a GC seems to be unaccceptable for you?
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.
On the contrary, the GC simplifies program for the vast majority of use cases. If it becomes a problem, then don't use it.
> And at the same time you loose valuable constructs such as RAII.
Forgive me for being argumentative, but have you looked at the D website? D has RAII, as advertised on the website homepage.
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.
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).
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.
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.
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++.
(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.)
(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: (defun html (string)
(string-upcase string))
(define-compiler-macro html (&whole form string)
(if (constantp string)
`',(string-upcase string)
form))
(defun foo (arg)
(print (html arg))
(print (html "compile-time"))
nil)
(foo "run-time")
; "RUN-TIME"
; "COMPILE-TIME"
(disassemble 'foo)
; disassembly for FOO
; Size: 128 bytes. Origin: #x10106A207D
; 7D: 498B4C2460 MOV RCX, [R12+96] ; thread.binding-stack-pointer
; no-arg-parsing entry point
; 82: 48894DF8 MOV [RBP-8], RCX
; 86: 488D5C24F0 LEA RBX, [RSP-16]
; 8B: 4883EC18 SUB RSP, 24
; 8F: 488B55F0 MOV RDX, [RBP-16]
; 93: 488B0576FFFFFF MOV RAX, [RIP-138] ; #<SB-KERNEL:FDEFN HTML>
; 9A: B902000000 MOV ECX, 2
; 9F: 48892B MOV [RBX], RBP
; A2: 488BEB MOV RBP, RBX
; A5: FF5009 CALL QWORD PTR [RAX+9]
; A8: 480F42E3 CMOVB RSP, RBX
; AC: 488D5C24F0 LEA RBX, [RSP-16]
; B1: 4883EC18 SUB RSP, 24
; B5: 488B055CFFFFFF MOV RAX, [RIP-164] ; #<SB-KERNEL:FDEFN COMMON-LISP:PRINT>
; BC: B902000000 MOV ECX, 2
; C1: 48892B MOV [RBX], RBP
; C4: 488BEB MOV RBP, RBX
; C7: FF5009 CALL QWORD PTR [RAX+9]
; CA: 488D5C24F0 LEA RBX, [RSP-16]
; CF: 4883EC18 SUB RSP, 24
; D3: 488B1546FFFFFF MOV RDX, [RIP-186] ; "COMPILE-TIME"
; DA: 488B0537FFFFFF MOV RAX, [RIP-201] ; #<SB-KERNEL:FDEFN COMMON-LISP:PRINT>
; E1: B902000000 MOV ECX, 2
; E6: 48892B MOV [RBX], RBP
; E9: 488BEB MOV RBP, RBX
; EC: FF5009 CALL QWORD PTR [RAX+9]
; EF: BA17001020 MOV EDX, 537919511
; F4: 488BE5 MOV RSP, RBP
; F7: F8 CLC
; F8: 5D POP RBP
; F9: C3 RET
; FA: 0F0B10 BREAK 16 ; Invalid argument count trap
`html` is only called once, to handle the call that isn't known at compile time. (eval-when (:compile-toplevel)
...)
You can also have read-time macros or load-time values, depending on what you need.