I’ll lead with a file:
// stdlib.dpp #include <stdio.h> #include <stdlib.h> void main() { printf("Hello world\n".ptr); enum numInts = 4; auto ints = cast(int*) malloc(int.sizeof * numInts); scope(exit) free(ints); foreach(int i; 0 .. numInts) { ints[i] = i; printf("ints[%d]: %d ".ptr, i, ints[i]); } printf("\n".ptr); }
The keen eye will notice that, except for the two include directives, the file is just plain D code. Let’s build and run it:
% d++ stdlib.dpp % ./stdlib Hello world ints[0]: 0 ints[1]: 1 ints[2]: 2 ints[3]: 3
Wait, what just happened?
You just saw a D file directly #include two headers from the C standard library and call two functions from them, which was then compiled and run. And it worked!
Why? I mean, just… why?
I’ve argued before that #include is C++’s killer feature. Interfacing to existing C or C++ libraries is, for me, C++’s only remaining use case. You include the relevant headers, and off you go. No bindings, no nonsense, it just works. As a D fan, I envied that. So this is my attempt to eliminate that “last” (again, for me, reasonable people may disagree) use case where one would reach for C++ as the weapon of choice.
There’s a reason C++ became popular. Upgrading to it from C was a decision with essentially 0 risk. I wanted that “just works” feature for D.
How?
d++ is a compiler wrapper. By default it uses dmd to compile the D code, but that’s configurable through the –compiler option. But dmd can’t compile code with #include directives in it (the lexer won’t even like it!), so what gives?
d++ will go through a .dpp file, and upon encountering an #include directive it expands it in-place, similarly to what would happen with a C or C++ compiler. Differently from clang or gcc however, the header file can’t just be inserted in, since the syntax of the declarations is in a different language. So it uses libclang to parse the header, and translates all of the declarations on the fly. This is trickier than it sounds since C and C++ allow for things that aren’t valid in D.
There’s one piece of the usability puzzle that’s missing from that story: the preprocessor. C header files have declarations but also macros, and some of those are necessary to use the library as it was intended. One can try and emulate this with CTFE functions in D, and sometimes it works. But I don’t want “sometimes”, I want guarantees, and the only way to do that is… to use the C preprocessor.
Blasphemy, I know. But since worse is better, d++ redefines all macros in the included header file so they’re available for use by the D program. It then runs the C preprocessor on the result of expanding all the #include directives, and the final result is a regular D file that can be compiled by dmd.
What next?
Bug fixing and C++ support. I won’t be happy until this works:
#include <vector> void main() { auto v = std.vector!int(); v.push_back(42); }
Code or it didn’t happen
I almost forgot: https://github.com/atilaneves/dpp.
WOW!
Thats already really awesome when interfacing C, but for C++ it seems an impossibly huge amount of work (see the abandoned Calypso project by Elie Morisse as an example). Hence, my advice is to first concentrate on supporting C really well and only then approach C++. That is, if your goal is to provide a tool for the broader D community.
Abandoned? ~30 commits in the last month…
Calypso isn’t abandoned. Elie is actively working on it.
I’m definitely concentrating on C first. I’ve gone over the work that needs to be done for C++ and I don’t actually think it’ll be that bad. Famous last words? We’ll see!
[…] the example he presented in the blog post accompanying the initial […]
[…] However, I wrote a little project called dpp because I’m lazy and don’t want to hand-translate C to D. Envious of C++’s and Objective C’s credible claim to be the only languages that can seamlessly interoperate with C (due to header inclusion and compatible syntax), I tried to replicate the experience in the D world. Using dpp, one can #include C headers in what would otherwise be D code and use it as one would in C++, even going to the point of supporting preprocessor macros. I wrote about the project in a different blog post. […]