What is ABI compatiblity? in a nutshell it let's you use libraries from another programming language. In D, all you need is a D file that describes the C/C++ library as a wrapper so the D compiler knows what you are using.
This means D will build upon everything that already exists for C/C++. You can intermix C/C++ and D in the same project if you so choose to. Which is really a big deal because you don't necessarily have to stop and rewrite years of code you might already have to start using D.
Syntax wise D is a kin to C#/Java, so if you know C# or Java, jumping over to using D is just a matter of learning the language specific Gotchas and core libraries. So the learning curve is quite low if you know C/C++,C#, or Java.
to see what D libraries are out there I suggest looking at http://code.dlang.org/
Said compiler must somehow work together with the D compiler here, which should lead to god-awful memory use, compilation time, and error messages.
So you have a C++ file that uses vector<int>, vector<struct c_struct>, vector<CppClass>, vector<DClass>, etc. once and then you can link to them from D code.
The only case where that doesn't work is when you have lots of one-off instantiations, where requiring an instantiation in C++ code for each use in D would get annoying quickly.
Do you have to write some kind of D header file for foreign C++ functions? Otherwise, how would D even know about the existence of e.g. vector<int>::push?
You will need to recreate the C++ function definitions in D and mark them extern(C++), but that's about it.
Let's be clear a compiler builds a object files that have to be linked to create a executable binary. IE compiler makes .o files. Linker takes output object files from the compiler an puts them (.o,.so/.dll,.a) together, to make .exe files.
This all works because you are linking to the C/C++ library binaries (the .o, .so, .a, .dll) files generated by a C/C++ compiling/linking. In most cases this is shared object library/dll provided by the operating system or pre-built library you install on the system. There is no need to compile it because it is pre-built. if you do compile the library yourself, it is a one time thing.
In D you create a .d file that describes what is in a C++ library .o/.so/.a/.dll file. So that upon linking, you take your D code's .o files + the C/C++ .o files to put together a full executable binary.
Templates are delivered as C++ include files (i.e. source code). When you use them, the compiler fills in the types you want and compiles the result.
For example, there's the std::vector<> template. You can fill in any type that meets the specific requirements of the template and use it like std::vector<int> or std::vector<YourC++ClassType>.
However, you probably won't be able to do std::vector<YourDClassType>. ABI compatibility is not enough to do this as you would be effectively mixing C++ and D source code here.
I think this should be working. You don't have to 'mix source code', you can implement YourDClassType in D and use it in C++. You simply have to write a C++ header describing the class. Then write a D file describing the external C++ sdt::vector<YourDClassType> file and link everything together.
Not sure if anybody actively tested this though ;-)
Also if your "library" is all templates, it's not a library, it's a framework that uses other libraries. There still has to be at some point tangible code that is compiled to an object file to be linked. You will have to find all of the dependancies and make sure that they are also described in a .d file.
This might mean you have a bunch of work that you need to do in order use the c++ code you want to bring in into D. But then again, if it's a public/open source library, and you go down that rabbit hole and make it work, you just made D even better.
please see https://dlang.org/spec/cpp_interface.html#cpp-templates for more details.
TIL STL is not a library.
How does it work in practice ? C++ name mangling is not standardized and so every compiler can implement its own scheme. So does it mean there is a list of D compatible c++ compiler somewhere ? I have the feeling that if you are using some exotic proprietary c++ compiler it won't work well.
That's right, and the D compiler deals with that by customizing the C++ mangling for every platform.
Want function programming and purity? Check.
Want C style/low abstraction code? Check.
Want extreme C++ metaprogramming? Check.
Want C#'s LINQ? Check.
Want an improved version of C++'s STL? Check.
Want low cognitive load memory management through a GC? Check.
Want highly tailored memory management? Check.
Want high level object oriented abstractions? Check.
Want memory safety? Check.
Want systems programming? Check.
Want rapid prototyping? Check.
D, fundamentally, assumes the programmer knows what approach they should take and lets them do it. There are no "we know better" design decisions in the language. I think this might be because D is so community driven. With no real company backing D was left in the hands of enthusiasts coming from all sorts of different backgrounds to implement ideas they liked.
That's a major problem, and not a feature.
One of C++'s main drawbacks is its size and arcane features, to the point that the language is known for being impossible to master. If all D brings to the table is an agenda to pick off C++'s complexity and drive it up even further then I fail to see what problems that will solve while it creates many others.
And built in at the compiler level, so it happens when you compile your program as you normally would, the tests show up while compiling, and the output does not contain the code. Always impressed me.
auto names = [ "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David"];
names.filter!(a => a.length == 5)
.array // convert from lazy range to array so we can sort
.sort!()
.map!(a => a.asUpperCase)
.joiner("\n")
.writeln;
Ranges enable lazy processing with efficient static dispatch against arbitrary types of ranges. Those individual algorithm functions are basically all template functions that return types tailored to match the input which allows the lazy evaluation to work. When writeln asks for the first element to print it asks joiner which asks map which asks asUpperCase and so on. The results are calculated upon request, not in advance, which helps you forgo a lot of memory allocations for storing temporary results.UFCS lets you call a function as if it were a member of the first parameter (i.e. fun(x, y) -> x.fun(y)). This lets you write it as if it were chain rather than a series of inside out function calls (i.e. `writeln(joiner(map!(a => asUpperCase(a)(sort!()(array(filter!(a => a.length == 5)(names)))), "\n"))`).
There is exactly two memory allocations in all of that. Once for the initial array and again prior to sorting because it's not reasonable to sort a lazy range. We could have reused the initial array by eagerly removing the items being filtered from it if we wanted.
And yes, templates are so much better in D than in C++ or in C# :)
How does @nogc work in D? Is it easy to keep track of what needs freeing and what does not or is it easy to mix up and get hard bugs? Also, what do these bugs look like? Is use-after-free possible or how is the failure mode i that case? Is it possible to call free on an object after it's been garbage collected?
The answer to all of your questions is basically the same as they are in C and C++. D does have @safe though which prevents unsafe memory operations and Walter is in the process of ratcheting up the memory safety with DIP1000[2].
1. https://dlang.org/phobos/std_experimental_allocator.html 2. https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.md
@safe:
at the top of your code, it'll be memory safe (excluding issues which we plug 'em when we find 'em). I wouldn't say that makes it fundamentally different from Rust. What is fundamentally different from Rust is the approach D uses to implement memory safety.So it is up to the community if they want to accept such change.
I should note that they still need some help cleaning the standard library and compiler corner cases in regard to @safe.
As always in such cases, to validate binary libraries, they need some kind of metadata to indicate they are safe libraries (aka they only use of @safe or @thrusted code).
.NET does this with MSIL metadata, Modula-3 does it directly on the module definition section, for example.
GC + range checks provide memory safety to most programs without the kind of extra work you need in Rust. This is a big reason why they are so common in industry.
The next option is to use reference counting by wrapping your data in Rc/Arc (depending on whether you need atomicity or not).
But that can still leak memory if you have cyclic data structures and can't break the cycle with Weak pointers.
At this point, what you need is a garbage collector.