First up, here’s a big disclaimer if the title didn’t warn you enough: this is a hack. It’s just a
proof-of-concept for getting
extern(C++) classes working with betterC D. Also, DMD keeps getting
better quickly, so if you’re reading this post when something more recent than version 2.080 is out, this hack is
probably obsolete. Hopefully you’ll find this post interesting anyway if you’re either
- Interested in all things
- Curious about how stuff like classes work in D.
If you haven’t read my earlier post about how polymorphism and inheritance work yet, I recommend doing that first.
Recap on Classes in D
In C++, there’s no essential difference between a
struct and a
class — they’re both
like C structs with extra features. In particular, they support polymorphic inheritance, which requires hidden
vtables. In D, a
struct is like a plain, non-inheritable C struct, while a
class is a
more powerful thing that inherits from a base
Object class. The
Object class implements
lots of convenient functionality like hash function support. In short, D structs are lower-level than C++
structs/classes, but D classes are higher-level.
It’s actually possible to get C++-style classes in D by marking the declaration with
extern(C++) class supports inheritance, but it doesn’t automatically inherit from D’s
The Problem and Plan of Attack
Object class is defined in the D runtime, so regular D classes don’t work in
-betterC. In theory,
extern(C++) classes should work in
-betterC, but they
don’t with the current version of DMD (version 2.080) because
extern(C++) classes still rely on
runtime type information (RTTI). RTTI is implemented using the
TypeInfo D class, which creates a
Object. Although this is only a link-time dependency, DMD barfs if it tries to compile
a class definition with
-betterC. (This is a good thing because compiler errors are much friendlier
than linker errors, but it’s relevant to the hack.)
I did some experimenting, and the only real dependency on RTTI that I found was in class instance construction. Specifically, when constructing a new class instance, D initialises the memory used by the instance before calling the user-written constructor. This initialisation populates things like vtable pointers and default values for members (as defined in the class definition). It actually works by just blitting from a sample that’s stored in RTTI.
There’s one other little problem: destructors in D aren’t inherently virtual or recursive (in normal D code, the runtime emulates virtualness and recursiveness using RTTI). Making our own virtual and recursive destructors is easy (but a minor nuisance). You could use a template mixin to reduce the boilerplate, but I just wrote out the definitions explicitly for this example.
Here’s a high-level view of the workaround:
- Avoid the compiler error by putting the class definitions and related stuff into a separate file that’s
- Reimplement class construction and destruction without RTTI.
- Do linker hacking to make up for the loss of
- Link with code that’s compiled normally with
The Class Code
Here’s the code for the classes. It’s a silly example that demonstrates
- A base class and a class that derives from it
- Constructors/destructors with arguments and side effects
- Virtual and non-virtual methods
- Member variables
Next there’s some templated factory code that does the construction, with a corresponding templated
convenience function for destruction. This code is
@nogc, so memory is managed using standard C
The tricky part is correctly initialising the raw memory before calling the constructor. D’s ABI for class layout is specced out, but I dodged the issue
completely with a little hack. For the purposes of this PoC, I made each class constructable with a
side-effect-free default constructor. The factory function contains a
static immutable instance of
the class. The raw memory of this “model” instance gets blitted into the newly allocated instance memory, and
we’ll know that we’ll have all the right vtable pointers, etc, in the right place. After that, we can safely call
the normal class constructor on the new instance. Thanks to D’s CTFE, the model instance gets constructed at
compile time, so any runtime library dependencies can be ripped out before linking with
code. Effectively, we temporarily borrow the standard class construction code during compilation.
By the way, C++ classes have value semantics, but
extern(C++) classes in D still have reference
semantics, and a class instance reference is identical to a pointer at the binary level.
There were two things I had to do to get this code to link with
-betterC code. First, DMD
inserted a reference to a
TypeInfo vtable from the runtime in an inconvenient place in the compiled
code. Disassembly showed it wasn’t truly needed, so I just made the linker happy by inventing a dummy
The User Code
Here’s the code that uses the classes. Again, it’s just a silly example that uses a bunch of class
functionality. However, this time it’s hackless code that compiles cleanly with
And it works!