Improve this page Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone. Page wiki View or edit the community-maintained wiki page associated with this page.

Interfacing to C++

While D is fully capable of interfacing to C, its ability to interface to C++ is much more limited. There are three ways to do it:

  1. Use C++'s ability to create a C interface, and then use D's ability to interface with C to access that interface.
  2. Use C++'s ability to create a COM interface, and then use D's ability to interface with COM to access that interface.
  3. Use the limited ability described here to connect directly to C++ functions and classes.

The General Idea

Being 100% compatible with C++ means more or less adding a fully functional C++ compiler front end to D. Anecdotal evidence suggests that writing such is a minimum of a 10 man-year project, essentially making a D compiler with such capability unimplementable. Other languages looking to hook up to C++ face the same problem, and the solutions have been:

  1. Support the COM interface (but that only works for Windows).
  2. Laboriously construct a C wrapper around the C++ code.
  3. Use an automated tool such as SWIG to construct a C wrapper.
  4. Reimplement the C++ code in the other language.
  5. Give up.

D takes a pragmatic approach that assumes a couple modest accommodations can solve a significant chunk of the problem:

Calling C++ Global Functions From D

Given a C++ function in a C++ source file:

#include <iostream>

using namespace std;

int foo(int i, int j, int k) {
  cout << "i = " << i << endl;
  cout << "j = " << j << endl;
  cout << "k = " << k << endl;

  return 7;
}

In the corresponding D code, foo is declared as having C++ linkage and function calling conventions:

extern (C++) int foo(int i, int j, int k);

and then it can be called within the D code:

extern (C++) int foo(int i, int j, int k);

void main() {
  foo(1,2,3);
}

Compiling the two files, the first with a C++ compiler, the second with a D compiler, linking them together, and then running it yields:

i = 1
j = 2
k = 3

There are several things going on here:

C++ functions that reside in namespaces cannot be direcly called from D.

Calling Global D Functions From C++

To make a D function accessible from C++, give it C++ linkage:

import std.stdio;

extern (C++) int foo(int i, int j, int k) {
  writefln("i = %s", i);
  writefln("j = %s", j);
  writefln("k = %s", k);
  return 1;
}

extern (C++) void bar();

void main() {
  bar();
}

The C++ end looks like:

int foo(int i, int j, int k);

void bar() {
  foo(6, 7, 8);
}

Compiling, linking, and running produces the output:

i = 6
j = 7
k = 8

Classes

D classes are singly rooted by Object, and have an incompatible layout from C++ classes. D interfaces, however, are very similar to C++ single inheritance class heirarchies. So, a D interface with the attribute of extern (C++) will have a virtual function pointer table (vtbl[]) that exactly matches C++'s. A regular D interface has a vtbl[] that differs in that the first entry in the vtbl[] is a pointer to D's RTTI info, whereas in C++ the first entry points to the first virtual function.

Calling C++ Virtual Functions From D

Given C++ source code defining a class like:

#include <iostream>

using namespace std;

class D {
 public:
  virtual int bar(int i, int j, int k)
  {
    cout << "i = " << i << endl;
    cout << "j = " << j << endl;
    cout << "k = " << k << endl;
    return 8;
  }
};

D *getD() {
  D *d = new D();
  return d;
}

We can get at it from D code like:

extern (C++) {
  interface D {
    int bar(int i, int j, int k);
  }

  D getD();
}

void main() {
  D d = getD();
  d.bar(9,10,11);
}

Calling D Virtual Functions From C++

Given D code like:

extern (C++) int callE(E);

extern (C++) interface E {
  int bar(int i, int j, int k);
}

class F : E {
  extern (C++) int bar(int i, int j, int k)
  {
    writefln("i = %s", i);
    writefln("j = %s", j);
    writefln("k = %s", k);
    return 8;
  }
}

void main() {
  F f = new F();
  callE(f);
}

The C++ code to access it looks like:

class E {
 public:
  virtual int bar(int i, int j, int k);
};


int callE(E *e) {
  return e->bar(11,12,13);
}

Note:

Function Overloading

C++ and D follow different rules for function overloading. D source code, even when calling extern (C++) functions, will still follow D overloading rules.

Storage Allocation

C++ code explicitly manages memory with calls to ::operator new() and ::operator delete(). D allocates memory using the D garbage collector, so no explicit delete's are necessary. D's new and delete are not compatible with C++'s ::operator new and ::operator delete. Attempting to allocate memory with C++ ::operator new and deallocate it with D's delete, or vice versa, will result in miserable failure.

D can still explicitly allocate memory using std.c.stdlib.malloc() and std.c.stdlib.free(), these are useful for connecting to C++ functions that expect malloc'd buffers, etc.

If pointers to D garbage collector allocated memory are passed to C++ functions, it's critical to ensure that that memory will not be collected by the garbage collector before the C++ function is done with it. This is accomplished by:

An interior pointer to the allocated memory block is sufficient to let the GC know the object is in use; i.e. it is not necessary to maintain a pointer to the beginning of the allocated memory.

The garbage collector does not scan the stacks of threads not created by the D Thread interface. Nor does it scan the data segments of other DLL's, etc.

Data Type Compatibility

D And C Type Equivalence
D typeC type
void void
byte signed char
ubyte unsigned char
char char (chars are unsigned in D)
wchar wchar_t (when sizeof(wchar_t) is 2)
dchar wchar_t (when sizeof(wchar_t) is 4)
short short
ushort unsigned short
int int
uint unsigned
long long long
ulong unsigned long long
float float
double double
real long double
ifloat no equivalent
idouble no equivalent
ireal no equivalent
cfloat no equivalent
cdouble no equivalent
creal no equivalent
struct struct
union union
enum enum
class no equivalent
type* type *
no equivalent type &
type[dim] type[dim]
type[dim]* type(*)[dim]
type[] no equivalent
type[type] no equivalent
type function(parameters) type(*)(parameters)
type delegate(parameters) no equivalent

These equivalents hold for most 32 bit C++ compilers. The C++ standard does not pin down the sizes of the types, so some care is needed.

Structs and Unions

D structs and unions are analogous to C's.

C code often adjusts the alignment and packing of struct members with a command line switch or with various implementation specific #pragma's. D supports explicit alignment attributes that correspond to the C compiler's rules. Check what alignment the C code is using, and explicitly set it for the D struct declaration.

D does not support bit fields. If needed, they can be emulated with shift and mask operations. htod will convert bit fields to inline functions that do the right shift and masks.

Object Construction and Destruction

Similarly to storage allocation and deallocation, objects constructed in D code should be destructed in D, and objects constructed in C++ should be destructed in C++ code.

Special Member Functions

D cannot call C++ special member functions, and vice versa. These include constructors, destructors, conversion operators, operator overloading, and allocators.

Runtime Type Identification

D runtime type identification uses completely different techniques than C++. The two are incompatible.

C++ Class Objects by Value

D can access POD (Plain Old Data) C++ structs, and it can access C++ class virtual functions by reference. It cannot access C++ classes by value.

C++ Templates

D templates have little in common with C++ templates, and it is very unlikely that any sort of reasonable method could be found to express C++ templates in a link-compatible way with D.

This means that the C++ STL, and C++ Boost, likely will never be accessible from D.

Exception Handling

D and C++ exception handling are completely different. Throwing exceptions across the boundaries between D and C++ code will likely not work.

Future Developments

How the upcoming C++1y standard will affect this is not known.

Over time, more aspects of the C++ ABI may be accessible directly from D.

Forums | Comments | Search | Downloads | Home