With D's low-level control, we can access any kind of data structure and paint nice types over it. To demonstrate this, we'll access a C++ object from D. The same technique can also be used for memory-mapped hardware.
Let's mimic the C++ object structure by executing the following steps:
extern(C++)
interface for the virtual table, like we did for the regular interaction between D and C++. The interface should inherit from the parent class' virtual function table interface.void*
member that represents the virtual table.this
pointer casted to the virtual interface type.alias this
with the property we wrote in step 4.pragma(mangle)
to assign it a matching name. You may have to figure out the C++ mangles experimentally; compile the C++ application and inspect its object file.The following is the C++ code to define the object:
class Class { public: Class(int n); virtual void add(int a); void print(); int number; }; void Class::print() { printf("Number = %d ", number); } Class::Class(int n) { printf("constructed "); number = number; } void Class::add(int a) { number += a; }
The following is the D code to access the C++ object:
struct Class { extern(C++) interface Vtable { void add(int a); } void* vtbl; @property Vtable getVirtuals() { return cast(Vtable) &this; } alias getVirtuals this; extern(C++) void print(); int number; pragma(mangle, "_ZN5ClassC2Ei") extern(C++) void ctor(int a); this(int a) { ctor(a); } } void main() { Class c = Class(0); c.num = 10; c.add(10); c.print(); }
It will print the following output:
constructed Number = 20
Data is data as far as the computer hardware is concerned. If we can map a type to the same bits in memory and use it in the appropriate places, it will work, irrespective of whether that type declaration is written in C++, D, or any other language.
In addition to D's built-in C++ support and other similar features, such as constructors and destructors, we can use a few other features to make this smoother to use.
The first one is alias this
. When a member that is is requested is not in the object itself, or when an implicit conversion is requested, it attempts substituting the alias this
value instead. This allows method forwarding and controlled implicit conversion, which implements a kind of subtyping—this is very similar to class inheritance.
The second is pragma(mangle)
. This changes the name of the function in the object file. If we match calling convention, arguments, return value, and name, we can call the code. The computer doesn't care about anything beyond that! It would also be possible to write code that automatically mangles D functions with C++ names and passes the return value of that function to pragma(mangle)
. The function would be evaluated at compile time to build the name string. We'll look at these techniques later in the book. Here, we got the mangled name experimentally (in this case, it is correct for my version of g++ on Linux; your results may vary).
We also used a nested interface for the virtual function table. This simply keeps the outer namespace clean; it has no functionality difference.
18.227.111.208