$(DDOC $(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(SPEC_S Interfacing to C++,
$(DDOC_BLANKLINE )
$(HEADERNAV_TOC $(HEADERNAV_ITEM general_idea, The General Idea)
$(HEADERNAV_SUBITEMS global-functions, Global Functions,
$(HEADERNAV_ITEM calling_cpp_global_from_d, Calling C++ Global Functions from D)
$(HEADERNAV_ITEM calling_global_d_functions_from_cpp, Calling Global D Functions From C++)
)
$(HEADERNAV_ITEM cpp-namespaces, C++ Namespaces)
$(HEADERNAV_SUBITEMS classes, Classes,
$(HEADERNAV_ITEM using_cpp_classes_from_d, Using C++ Classes From D)
$(HEADERNAV_ITEM using_d_classes_from_cpp, Using D Classes From C++)
)
$(HEADERNAV_ITEM structs, Structs)
$(HEADERNAV_ITEM cpp-templates, C++ Templates)
$(HEADERNAV_ITEM function-overloading, Function Overloading)
$(HEADERNAV_ITEM memory-allocation, Memory Allocation)
$(HEADERNAV_SUBITEMS data-type-compatibility, Data Type Compatibility,
$(HEADERNAV_ITEM dynamic-arrays, Dynamic Arrays)
)
$(HEADERNAV_ITEM packing-and-alignment, Packing and Alignment)
$(HEADERNAV_ITEM lifetime-management, Lifetime Management)
$(HEADERNAV_ITEM special-member-functions, Special Member Functions)
$(HEADERNAV_ITEM rtti, Runtime Type Identification)
$(HEADERNAV_ITEM exception-handling, Exception Handling)
$(HEADERNAV_ITEM comparing-d-immutable-and-const-with-cpp-const, Comparing D Immutable and Const with C++ Const)
)
$(DDOC_BLANKLINE )
$(P This document specifies how to interface with C++ directly.)
$(DDOC_BLANKLINE )
$(P It is also possible to indirectly interface with C++ code, either
through a $(DDLINK spec/interfaceToC, Interfacing to C, C interface) or a
COM interface.)
$(DDOC_BLANKLINE )
$(LNAME2 general_idea, The General Idea)
$(DDOC_BLANKLINE )
$(P 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:
)
$(DDOC_BLANKLINE )
$(OL $(LI Support the COM interface (but that only works for Windows).)
$(LI Laboriously construct a C wrapper around
the C++ code.)
$(LI Use an automated tool such as SWIG to construct a
C wrapper.)
$(LI Reimplement the C++ code in the other language.)
$(LI Give up.)
)
$(DDOC_BLANKLINE )
$(P D takes a pragmatic approach that assumes a couple
modest accommodations can solve a significant chunk of
the problem:
)
$(DDOC_BLANKLINE )
$(UL $(LI matching C++ name mangling conventions)
$(LI matching C++ function calling conventions)
$(LI matching C++ virtual function table layout for single inheritance)
)
$(DDOC_BLANKLINE )
$(LNAME2 global-functions, Global Functions)
$(DDOC_BLANKLINE )
$(P C++ global functions, including those in namespaces, can be declared
and called in D, or defined in D and called in C++.)
$(DDOC_BLANKLINE )
$(LNAME2 calling_cpp_global_from_d, Calling C++ Global Functions from D)
$(DDOC_BLANKLINE )
$(P Given a C++ function in a C++ source file:)
$(DDOC_BLANKLINE )
$(CPPLISTING #include $(LT )iostream$(GT )
$(DDOC_BLANKLINE )
using namespace std;
$(DDOC_BLANKLINE )
int foo(int i, int j, int k)
{
cout << "i = " << i << endl;
cout << "j = " << j << endl;
cout << "k = " << k << endl;
$(DDOC_BLANKLINE )
return 7;
}
)
$(DDOC_BLANKLINE )
$(P In the corresponding D code, $(CODE foo)
is declared as having C++ linkage and function calling conventions:
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C++) $(D_KEYWORD int) foo($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k);
)
$(DDOC_BLANKLINE )
$(P and then it can be called within the D code:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C++) $(D_KEYWORD int) foo($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k);
$(D_KEYWORD void) main()
{
foo(1, 2, 3);
}
)
$(DDOC_BLANKLINE )
$(P Compiling the two files, the first with a C++ compiler,
the second with a D compiler, linking them together,
and then running it yields:)
$(DDOC_BLANKLINE )
$(CONSOLE $(GT ) g++ -c foo.cpp
$(GT ) dmd bar.d foo.o -L-lstdc++ && ./bar
i = 1
j = 2
k = 3
)
$(DDOC_BLANKLINE )
$(P There are several things going on here:)
$(DDOC_BLANKLINE )
$(UL $(LI D understands how C++ function names are "mangled" and the
correct C++ function call/return sequence.)
$(DDOC_BLANKLINE )
$(LI Because modules are not part of C++, each function with C++ linkage
in the global namespace must be globally unique within the program.)
$(DDOC_BLANKLINE )
$(LI There are no $(D __cdecl), $(D __far), $(D __stdcall), $(D __declspec), or other
such nonstandard C++ extensions in D.)
$(DDOC_BLANKLINE )
$(LI There are no volatile type modifiers in D.)
$(DDOC_BLANKLINE )
$(LI Strings are not 0 terminated in D. See "Data Type Compatibility"
for more information about this. However, string literals in D are
0 terminated.)
$(DDOC_BLANKLINE )
)
$(DDOC_BLANKLINE )
$(LNAME2 calling_global_d_functions_from_cpp, Calling Global D Functions From C++)
$(DDOC_BLANKLINE )
$(P To make a D function accessible from C++, give it
C++ linkage:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) std.stdio;
$(D_KEYWORD extern) (C++) $(D_KEYWORD int) foo($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k)
{
writefln($(D_STRING "i = %s"), i);
writefln($(D_STRING "j = %s"), j);
writefln($(D_STRING "k = %s"), k);
$(D_KEYWORD return) 1;
}
$(D_KEYWORD extern) (C++) $(D_KEYWORD void) bar();
$(D_KEYWORD void) main()
{
bar();
}
)
$(DDOC_BLANKLINE )
$(P The C++ end looks like:)
$(DDOC_BLANKLINE )
$(CPPLISTING int foo(int i, int j, int k);
$(DDOC_BLANKLINE )
void bar()
{
foo(6, 7, 8);
}
)
$(DDOC_BLANKLINE )
$(P Compiling, linking, and running produces the output:)
$(DDOC_BLANKLINE )
$(CONSOLE $(GT ) dmd -c foo.d
$(GT ) g++ bar.cpp foo.o -lphobos2 -pthread -o bar && ./bar
i = 6
j = 7
k = 8
)
$(DDOC_BLANKLINE )
$(LNAME2 cpp-namespaces, C++ Namespaces)
$(DDOC_BLANKLINE )
$(P C++ symbols that reside in namespaces can be
accessed from D. A $(LINK2 attribute.html#namespace, namespace)
can be added to the extern (C++)
$(LINK2 attribute.html#linkage, LinkageAttribute):
)
$(D_CODE $(D_KEYWORD extern) (C++, N) $(D_KEYWORD int) foo($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k);
$(D_KEYWORD void) main()
{
N.foo(1, 2, 3); $(D_COMMENT // foo is in C++ namespace 'N'
)}
)
$(DDOC_BLANKLINE )
$(P C++ can open the same namespace in the same file and multiple files.
In D, this can be done as follows:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD module) ns;
$(D_KEYWORD extern) (C++, $(D_STRING `ns`))
{
$(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; }
}
)
$(P Any expression that resolves to either a tuple of strings or an empty tuple is accepted.
When the expression resolves to an empty tuple, it is equivalent to extern (C++)
)
$(D_CODE $(D_KEYWORD extern)(C++, (expression))
{
$(D_KEYWORD int) bar() { $(D_KEYWORD return) 2; }
}
)
$(DDOC_BLANKLINE )
$(P or in multiple files, by organizing them in a package consisting of several modules:)
$(D_CODE ns/
|-- a.d
|-- b.d
|-- $(D_KEYWORD package).d
)
$(DDOC_BLANKLINE )
$(P File ns/a.d
:)
$(D_CODE $(D_KEYWORD module) a; $(D_KEYWORD extern) (C++, $(D_STRING `ns`)) { $(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; } }
)
$(DDOC_BLANKLINE )
$(P File ns/b.d
:)
$(D_CODE $(D_KEYWORD module) b; $(D_KEYWORD extern) (C++, $(D_STRING `ns`)) { $(D_KEYWORD int) bar() { $(D_KEYWORD return) 2; } }
)
$(DDOC_BLANKLINE )
$(P File ns/package.d
:)
$(D_CODE $(D_KEYWORD module) ns;
$(D_KEYWORD public) $(D_KEYWORD import) a, b;
)
$(P Then import the package containing the extern C++ declarations as follows:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) ns;
$(D_KEYWORD static) $(D_KEYWORD assert)(foo() == 1 && bar() == 2);
)
$(DDOC_BLANKLINE )
$(P Note that the extern $(LPAREN)C++,
ns$(RPAREN )
linkage attribute affects only the ABI (name mangling and calling convention) of
these declarations. Importing them follows the usual
$(LINK2 spec/module, D module import semantics).)
$(DDOC_BLANKLINE )
$(P Alternatively, the non-string form can be used to introduce a scope. Note that the
enclosing module already provides a scope for the symbols declared in the namespace.
This form does not allow closing and reopening the same namespace with in the same module. That is:)
$(D_CODE $(D_KEYWORD module) a; $(D_KEYWORD extern) (C++, ns1) { $(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; } }
)
$(D_CODE $(D_KEYWORD module) b; $(D_KEYWORD extern) (C++, ns1) { $(D_KEYWORD int) bar() { $(D_KEYWORD return) 2; } }
)
$(D_CODE $(D_KEYWORD import) a, b;
$(D_KEYWORD static) $(D_KEYWORD assert)(foo() == 1 && bar() == 2);
)
$(P works, but:)
$(D_CODE $(D_KEYWORD extern) (C++, ns1) { $(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; } }
$(D_KEYWORD extern) (C++, ns1) { $(D_KEYWORD int) bar() { $(D_KEYWORD return) 2; } }
)
$(P does not. Additionally, aliases can be used to avoid collision of symbols:)
$(D_CODE $(D_KEYWORD module) a; $(D_KEYWORD extern) (C++, ns) { $(D_KEYWORD int) foo() { $(D_KEYWORD return) 1; } }
)
$(D_CODE $(D_KEYWORD module) b; $(D_KEYWORD extern) (C++, ns) { $(D_KEYWORD int) bar() { $(D_KEYWORD return) 2; } }
)
$(D_CODE $(D_KEYWORD module) ns;
$(D_KEYWORD import) a, b;
$(D_KEYWORD alias) foo = a.ns.foo;
$(D_KEYWORD alias) bar = b.ns.bar;
)
$(D_CODE $(D_KEYWORD import) ns;
$(D_KEYWORD static) $(D_KEYWORD assert)(foo() == 1 && bar() == 2);
)
$(DDOC_BLANKLINE )
$(LNAME2 classes, Classes)
$(DDOC_BLANKLINE )
$(P C++ classes can be declared in D by using the $(CODE extern (C++))
attribute on $(CODE class), $(CODE struct) and $(CODE interface)
declarations. $(CODE extern (C++)) interfaces have the same restrictions as
D interfaces, which means that Multiple Inheritance is supported to the
extent that only one base class can have member fields.)
$(DDOC_BLANKLINE )
$(P $(CODE extern (C++)) structs do not support virtual functions but can
be used to map C++ value types.)
$(DDOC_BLANKLINE )
$(P Unlike classes and interfaces with D linkage, $(CODE extern (C++))
classes and interfaces are not rooted in $(CODE Object) and cannot be used
with $(CODE typeid).)
$(DDOC_BLANKLINE )
$(P D structs and classes have different semantics whereas C++ structs and
classes are basically the same. The use of a D struct or class depends on
the C++ implementation and not on the used C++ keyword.
When mapping a D $(CODE class) onto a C++ $(CODE struct),
use $(CODE extern(C++, struct)) to avoid linking problems with C++ compilers
(notably MSVC) that distinguish between C++'s $(CODE class) and $(CODE struct)
when mangling. Conversely, use $(CODE extern(C++, class)) to map a D
$(CODE struct) onto a C++ $(CODE class).)
$(DDOC_BLANKLINE )
$(P $(CODE extern(C++, class)) and $(CODE extern(C++, struct)) can be combined
with C++ namespaces:)
$(D_CODE $(D_KEYWORD extern) (C++, $(D_KEYWORD struct)) $(D_KEYWORD extern) (C++, foo)
$(D_KEYWORD class) Bar
{
}
)
$(DDOC_BLANKLINE )
$(LNAME2 using_cpp_classes_from_d, Using C++ Classes From D)
$(DDOC_BLANKLINE )
$(P The following example shows binding of a pure virtual function, its
implementation in a derived class, a non-virtual member function, and a
member field:)
$(DDOC_BLANKLINE )
$(CPPLISTING #include $(LT )iostream$(GT )
$(DDOC_BLANKLINE )
using namespace std;
$(DDOC_BLANKLINE )
class Base
{
public:
virtual void print3i(int a, int b, int c) = 0;
};
$(DDOC_BLANKLINE )
class Derived : public Base
{
public:
int field;
Derived(int field) : field(field) {}
$(DDOC_BLANKLINE )
void print3i(int a, int b, int c)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
$(DDOC_BLANKLINE )
int mul(int factor);
};
$(DDOC_BLANKLINE )
int Derived::mul(int factor)
{
return field * factor;
}
$(DDOC_BLANKLINE )
Derived *createInstance(int i)
{
return new Derived(i);
}
$(DDOC_BLANKLINE )
void deleteInstance(Derived *&d)
{
delete d;
d = 0;
}
)
$(DDOC_BLANKLINE )
$(P We can use it in D code like:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern)(C++)
{
$(D_KEYWORD abstract) $(D_KEYWORD class) Base
{
$(D_KEYWORD void) print3i($(D_KEYWORD int) a, $(D_KEYWORD int) b, $(D_KEYWORD int) c);
}
$(D_KEYWORD class) Derived : Base
{
$(D_KEYWORD int) field;
@disable $(D_KEYWORD this)();
$(D_KEYWORD override) $(D_KEYWORD void) print3i($(D_KEYWORD int) a, $(D_KEYWORD int) b, $(D_KEYWORD int) c);
$(D_KEYWORD final) $(D_KEYWORD int) mul($(D_KEYWORD int) factor);
}
Derived createInstance($(D_KEYWORD int) i);
$(D_KEYWORD void) deleteInstance($(D_KEYWORD ref) Derived d);
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD import) std.stdio;
$(D_KEYWORD auto) d1 = createInstance(5);
writeln(d1.field);
writeln(d1.mul(4));
Base b1 = d1;
b1.print3i(1, 2, 3);
deleteInstance(d1);
$(D_KEYWORD assert)(d1 $(D_KEYWORD is) $(D_KEYWORD null));
$(D_KEYWORD auto) d2 = createInstance(42);
writeln(d2.field);
deleteInstance(d2);
$(D_KEYWORD assert)(d2 $(D_KEYWORD is) $(D_KEYWORD null));
}
)
$(DDOC_BLANKLINE )
$(P Compiling, linking, and running produces the output:)
$(DDOC_BLANKLINE )
$(CONSOLE $(GT ) g++ base.cpp
$(GT ) dmd main.d base.o -L-lstdc++ && ./main
5
20
a = 1
b = 2
c = 3
42
)
$(DDOC_BLANKLINE )
$(P Note how in the above example, the constructor is not bindable and is
instead disabled on the D side; an alternative would be to reimplement the
constructor in D. See the $(DDSUBLINK spec/cpp_interface, lifetime-management,
section below on lifetime management) for more information.)
$(DDOC_BLANKLINE )
$(LNAME2 using_d_classes_from_cpp, Using D Classes From C++)
$(DDOC_BLANKLINE )
$(P Given D code like:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C++) $(D_KEYWORD int) callE(E);
$(D_KEYWORD extern) (C++) $(D_KEYWORD interface) E
{
$(D_KEYWORD int) bar($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k);
}
$(D_KEYWORD class) F : E
{
$(D_KEYWORD extern) (C++) $(D_KEYWORD int) bar($(D_KEYWORD int) i, $(D_KEYWORD int) j, $(D_KEYWORD int) k)
{
$(D_KEYWORD import) std.stdio : writefln;
writefln($(D_STRING "i = %s"), i);
writefln($(D_STRING "j = %s"), j);
writefln($(D_STRING "k = %s"), k);
$(D_KEYWORD return) 8;
}
}
$(D_KEYWORD void) main()
{
F f = $(D_KEYWORD new) F();
callE(f);
}
)
$(DDOC_BLANKLINE )
$(P The C++ code to access it looks like:)
$(DDOC_BLANKLINE )
$(CPPLISTING class E
{
public:
virtual int bar(int i, int j, int k);
};
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
int callE(E *e)
{
return e->bar(11, 12, 13);
}
)
$(DDOC_BLANKLINE )
$(CONSOLE $(GT ) dmd -c base.d
$(GT ) g++ klass.cpp base.o -lphobos2 -pthread -o klass && ./klass
i = 11
j = 12
k = 13
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 structs, Structs)
$(DDOC_BLANKLINE )
$(P C++ allows a struct to inherit from a base struct. This is done in D using
alias this
:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) Base { ... members ... };
$(D_KEYWORD struct) Derived
{
Base base; $(D_COMMENT // make it the first field
) $(D_KEYWORD alias) base $(D_KEYWORD this);
... members ...
}
)
$(DDOC_BLANKLINE )
$(P In both C++ and D, if a struct has zero fields, the struct still has a
size of 1 byte. But, in C++ if the struct with zero fields is used as a base
struct, its size is zero (called the
$(LINK2 https://en.cppreference.com/w/cpp/language/ebo, Empty Base Optimization)).
There are two methods for emulating this behavior in D.
The first forwards references to a function returning a faked reference to the base:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) Base { ... members ... };
$(D_KEYWORD struct) DerivedStruct
{
$(D_KEYWORD static) $(D_KEYWORD if) (Base.tupleof.length > 0)
Base base;
$(D_KEYWORD else)
$(D_KEYWORD ref) $(D_KEYWORD inout)(Base) base() $(D_KEYWORD inout)
{
$(D_KEYWORD return) *$(D_KEYWORD cast)($(D_KEYWORD inout)(Base)*)&$(D_KEYWORD this);
}
$(D_KEYWORD alias) base $(D_KEYWORD this);
... members ...
}
)
$(DDOC_BLANKLINE )
$(P The second makes use of template mixins:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD mixin) $(D_KEYWORD template) BaseMembers()
{
$(D_KEYWORD void) memberFunction() { ... }
}
$(D_KEYWORD struct) Base
{
$(D_KEYWORD mixin) BaseMembers!();
}
$(D_KEYWORD struct) Derived
{
$(D_KEYWORD mixin) BaseMembers!();
... members ...
}
)
$(DDOC_BLANKLINE )
$(P Note that the template mixin is evaluated in the context of its
instantiation, not declaration. If this is a problem, the template mixin
can use local imports, or have the member functions forward to the
actual functions.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 cpp-templates, C++ Templates)
$(DDOC_BLANKLINE )
$(P C++ function and type templates can be bound by using the
$(CODE extern (C++)) attribute on a function or type template declaration.)
$(DDOC_BLANKLINE )
$(P Note that all instantiations used in D code must be provided by linking
to C++ object code or shared libraries containing the instantiations.)
$(DDOC_BLANKLINE )
$(P For example:)
$(DDOC_BLANKLINE )
$(CPPLISTING #include $(LT )iostream$(GT )
$(DDOC_BLANKLINE )
template$(LT )class T$(GT )
struct Foo
{
private:
T field;
$(DDOC_BLANKLINE )
public:
Foo(T t) : field(t) {}
T get();
void set(T t);
};
$(DDOC_BLANKLINE )
template$(LT )class T$(GT )
T Foo$(LT )T$(GT )::get()
{
return field;
}
$(DDOC_BLANKLINE )
template$(LT )class T$(GT )
void Foo$(LT )T$(GT )::set(T t)
{
field = t;
}
$(DDOC_BLANKLINE )
Foo$(LT )int$(GT ) makeIntFoo(int i)
{
return Foo$(LT )int$(GT )(i);
}
$(DDOC_BLANKLINE )
Foo$(LT )char$(GT ) makeCharFoo(char c)
{
return Foo$(LT )char$(GT )(c);
}
$(DDOC_BLANKLINE )
template$(LT )class T$(GT )
void increment(Foo$(LT )T$(GT ) &foo)
{
foo.set(foo.get() + 1);
}
$(DDOC_BLANKLINE )
template$(LT )class T$(GT )
void printThreeNext(Foo$(LT )T$(GT ) foo)
{
for(size_t i = 0; i $(LT ) 3; ++i)
{
std::cout $(LT )$(LT ) foo.get() $(LT )$(LT ) std::endl;
increment(foo);
}
}
$(DDOC_BLANKLINE )
// The following two functions ensure that the required instantiations of
// printThreeNext are provided by this code module
void printThreeNexti(Foo$(LT )int$(GT ) foo)
{
printThreeNext(foo);
}
$(DDOC_BLANKLINE )
void printThreeNextc(Foo$(LT )char$(GT ) foo)
{
printThreeNext(foo);
}
)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern)(C++):
$(D_KEYWORD struct) Foo(T)
{
$(D_KEYWORD private):
T field;
$(D_KEYWORD public):
@disable $(D_KEYWORD this)();
T get();
$(D_KEYWORD void) set(T t);
}
Foo!$(D_KEYWORD int) makeIntFoo($(D_KEYWORD int) i);
Foo!$(D_KEYWORD char) makeCharFoo($(D_KEYWORD char) c);
$(D_KEYWORD void) increment(T)($(D_KEYWORD ref) Foo!T foo);
$(D_KEYWORD void) printThreeNext(T)(Foo!T foo);
$(D_KEYWORD extern)(D) $(D_KEYWORD void) main()
{
$(D_KEYWORD auto) i = makeIntFoo(42);
$(D_KEYWORD assert)(i.get() == 42);
i.set(1);
increment(i);
$(D_KEYWORD assert)(i.get() == 2);
$(D_KEYWORD auto) c = makeCharFoo('a');
increment(c);
$(D_KEYWORD assert)(c.get() == 'b');
c.set('A');
printThreeNext(c);
}
)
$(DDOC_BLANKLINE )
$(P Compiling, linking, and running produces the output:)
$(DDOC_BLANKLINE )
$(CONSOLE $(GT ) g++ -c template.cpp
$(GT ) dmd main.d template.o -L-lstdc++ && ./main
A
B
C
)
$(DDOC_BLANKLINE )
$(LNAME2 function-overloading, Function Overloading)
$(DDOC_BLANKLINE )
$(P C++ and D follow different rules for function overloading.
D source code, even when calling $(CODE extern (C++)) functions,
will still follow D overloading rules.
)
$(DDOC_BLANKLINE )
$(LNAME2 memory-allocation, Memory Allocation)
$(DDOC_BLANKLINE )
$(P C++ code explicitly manages memory with calls to
$(CODE ::operator new()) and $(CODE ::operator delete()).
D's $(CODE new) operator allocates memory using the D garbage collector,
so no explicit delete is necessary. D's $(CODE new) operator is not
compatible with C++'s $(CODE ::operator new) and $(CODE ::operator delete).
Attempting to allocate memory with D's $(CODE new) and deallocate with
C++ $(CODE ::operator delete) will result in miserable failure.
)
$(DDOC_BLANKLINE )
$(P D can explicitly manage memory using a variety of library tools, such as
with $(MREF std, experimental, allocator). Additionally,
$(CODE core.stdc.stdlib.malloc) and $(CODE core.stdc.stdlib.free) can be
used directly for connecting to C++ functions that expect $(CODE malloc)'d
buffers.
)
$(DDOC_BLANKLINE )
$(P If pointers to memory allocated on the D garbage collector heap are
passed to C++ functions, it's critical to ensure that the referenced memory
will not be collected by the D garbage collector before the C++ function is
done with it. This is accomplished by:
)
$(DDOC_BLANKLINE )
$(UL $(DDOC_BLANKLINE )
$(LI Making a copy of the data using
$(MREF std, experimental, allocator) or $(CODE core.stdc.stdlib.malloc)
and passing the copy instead.)
$(DDOC_BLANKLINE )
$(LI Leaving a pointer to it on the stack (as a parameter or
automatic variable), as the garbage collector will scan the stack.)
$(DDOC_BLANKLINE )
$(LI Leaving a pointer to it in the static data segment, as the
garbage collector will scan the static data segment.)
$(DDOC_BLANKLINE )
$(LI Registering the pointer with the garbage collector using the
$(CODE core.memory.GC.addRoot) or $(CODE core.memory.GC.addRange)
functions.)
$(DDOC_BLANKLINE )
)
$(DDOC_BLANKLINE )
$(P 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 $(I beginning) of the allocated memory.
)
$(DDOC_BLANKLINE )
$(P The garbage collector does not scan the stacks of threads not
registered with the D runtime, nor does it scan the data segments of
shared libraries that aren't registered with the D runtime.
)
$(DDOC_BLANKLINE )
$(LNAME2 data-type-compatibility, Data Type Compatibility)
$(DDOC_BLANKLINE )
$(TABLE2 D And C++ Type Equivalence,
$(DDOC_BLANKLINE )
D type, C++ type
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B void)),
$(ARGS $(B void))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B byte)),
$(ARGS $(B signed char))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B ubyte)),
$(ARGS $(B unsigned char))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B char)),
$(ARGS $(B char) (chars are unsigned in D))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(D core.stdc.stddef.wchar_t)),
$(ARGS $(D wchar_t))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B short)),
$(ARGS $(B short))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B ushort)),
$(ARGS $(B unsigned short))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B int)),
$(ARGS $(B int))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B uint)),
$(ARGS $(B unsigned))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B long)),
$(ARGS $(B long) if it is 64 bits wide, otherwise $(B long long))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B ulong)),
$(ARGS $(B unsigned long) if it is 64 bits wide, otherwise $(B unsigned long long))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(D core.stdc.config.cpp_long)),
$(ARGS $(B long))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(D core.stdc.config.cpp_ulong)),
$(ARGS $(B unsigned long))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B float)),
$(ARGS $(B float))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B double)),
$(ARGS $(B double))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B real)),
$(ARGS $(B long double))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(CODE extern (C++)) $(B struct)),
$(ARGS $(B struct) or $(B class))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(CODE extern (C++)) $(B class)),
$(ARGS $(B struct) or $(B class))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(CODE extern (C++)) $(B interface)),
$(ARGS $(B struct) or $(B class) with no member fields)
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B union)),
$(ARGS $(B union))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B enum)),
$(ARGS $(B enum))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type)$(B *)),
$(ARGS $(I type) $(B *))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(B ref) $(I type) (in parameter lists only)),
$(ARGS $(I type) $(CODE_AMP ))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type)$(B [)$(I dim)$(B ])),
$(ARGS $(I type)$(B [)$(I dim)$(B ]) for a variable/field declaration,
or $(DDSUBLINK spec/interfaceToC, passing_d_array, use ref
for function parameter))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type)$(B [)$(I dim)$(B ]*)),
$(ARGS $(I type)$(B (*)[)$(I dim)$(B ]))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type)$(B [])),
$(ARGS no extern (C++)
equivalent, $(RELATIVE_LINK2 dynamic-arrays, see below))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type)$(B [)$(I type)$(B ])),
$(ARGS no equivalent)
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type) $(B function)$(B $(LPAREN))$(I parameters)$(B $(RPAREN ))),
$(ARGS $(I type)$(B (*))$(B $(LPAREN))$(I parameters)$(B $(RPAREN )))
)
$(DDOC_BLANKLINE )
$(TROW $(ARGS $(I type) $(B delegate)$(B $(LPAREN))$(I parameters)$(B $(RPAREN ))),
$(ARGS no equivalent)
)
)
$(DDOC_BLANKLINE )
$(P These equivalents hold when the D and C++ compilers used are companions
on the host platform.)
$(DDOC_BLANKLINE )
$(LNAME2 dynamic-arrays, Dynamic Arrays)
$(DDOC_BLANKLINE )
$(P These are not supported for extern (C++)
. For extern (C)
, they
are equivalent to a struct template. For example:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) (C) $(D_KEYWORD const)($(D_KEYWORD char))[] slice;
)
$(DDOC_BLANKLINE )
$(P dmd -HC
generates the following C++ declaration:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD extern) $(D_STRING "C") _d_dynamicArray< $(D_KEYWORD const) $(D_KEYWORD char) > slice;
)
$(P _d_dynamicArray
is generated as follows:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_COMMENT /// Represents a D [] array
)$(D_KEYWORD template)<typename T>
$(D_KEYWORD struct) _d_dynamicArray $(D_KEYWORD final)
{
size_t length;
T *ptr;
_d_dynamicArray() : length(0), ptr(NULL) { }
_d_dynamicArray(size_t length_in, T *ptr_in)
: length(length_in), ptr(ptr_in) { }
T& operator[]($(D_KEYWORD const) size_t idx) {
$(D_KEYWORD assert)(idx < length);
$(D_KEYWORD return) ptr[idx];
}
$(D_KEYWORD const) T& operator[]($(D_KEYWORD const) size_t idx) $(D_KEYWORD const) {
$(D_KEYWORD assert)(idx < length);
$(D_KEYWORD return) ptr[idx];
}
};
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 packing-and-alignment, Packing and Alignment)
$(DDOC_BLANKLINE )
$(P D structs and unions are analogous to C's.
)
$(DDOC_BLANKLINE )
$(P C code often adjusts the alignment and packing of struct members
with a command line switch or with various implementation specific
$(HASH )pragmas. 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.
)
$(DDOC_BLANKLINE )
$(P D supports bitfields in the standard library: see
$(REF bitfields, std, bitmanip).
)
$(DDOC_BLANKLINE )
$(LNAME2 lifetime-management, Lifetime Management)
$(DDOC_BLANKLINE )
$(P C++ constructors, copy constructors, move constructors and destructors
cannot be called directly in D code, and D constructors, postblit operators
and destructors cannot be directly exported to C++ code. Interoperation of
types with these special operators is possible by either 1$(RPAREN )
disabling the operator in the client language and only using it in the host
language, or 2$(RPAREN ) faithfully reimplementing the operator in the
client language. With the latter approach, care needs to be taken to ensure
observable semantics remain the same with both implementations, which can be
difficult, or in some edge cases impossible, due to differences in how the
operators work in the two languages. For example, in D all objects are
movable and there is no move constructor.)
$(DDOC_BLANKLINE )
$(LNAME2 special-member-functions, Special Member Functions)
$(DDOC_BLANKLINE )
$(P D cannot directly call C++ special member functions, and vice versa.
These include constructors, destructors, conversion operators,
operator overloading, and allocators.
)
$(DDOC_BLANKLINE )
$(LNAME2 rtti, Runtime Type Identification)
$(DDOC_BLANKLINE )
$(P D runtime type identification
uses completely different techniques than C++.
The two are incompatible.)
$(DDOC_BLANKLINE )
$(LNAME2 exception-handling, Exception Handling)
$(DDOC_BLANKLINE )
$(P Exception interoperability is a work in progress.)
$(DDOC_BLANKLINE )
$(P At present, C++ exceptions cannot be caught in or thrown from D, and D
exceptions cannot be caught in or thrown from C++. Additionally, objects
in C++ stack frames are not guaranteed to be destroyed when unwinding the
stack due to a D exception, and vice versa.)
$(DDOC_BLANKLINE )
$(P The plan is to support all of the above except throwing D exceptions
directly in C++ code (but they will be throwable indirectly by calling into
a D function with C++ linkage).)
$(DDOC_BLANKLINE )
$(SECTION2 $(LNAME2 comparing-d-immutable-and-const-with-cpp-const, Comparing D Immutable and Const with C++ Const),
$(DDOC_BLANKLINE )
$(TABLE_SPECIAL $(ARGS Const, Immutable Comparison),
Feature, D, C++98
$(TROW $(D const) keyword, Yes, Yes)
$(TROW $(D immutable) keyword, Yes, No)
$(TROW const notation,
$(D_CODE $(D_COMMENT // Functional:
)$(D_COMMENT //ptr to const ptr to const int
)$(D_KEYWORD const)($(D_KEYWORD int)*)* p;
)
,
$(CPPLISTING // Postfix:
//ptr to const ptr to const int
const int *const *p;
)
)
$(DDOC_BLANKLINE )
$(TROW transitive const,
$(D_CODE $(D_COMMENT // Yes:
)$(D_COMMENT //const ptr to const ptr to const int
)$(D_KEYWORD const) $(D_KEYWORD int)** p;
**p = 3; $(D_COMMENT // error
))
,
$(CPPLISTING // No:
// const ptr to ptr to int
int** const p;
**p = 3; // ok
)
)
$(DDOC_BLANKLINE )
$(TROW cast away const,
$(D_CODE $(D_COMMENT // Yes:
)$(D_COMMENT // ptr to const int
)$(D_KEYWORD const)($(D_KEYWORD int))* p;
$(D_KEYWORD int)* q = $(D_KEYWORD cast)($(D_KEYWORD int)*)p; $(D_COMMENT // ok
))
,
$(CPPLISTING // Yes:
// ptr to const int
const int* p;
int* q = const_cast$(LT )int*$(GT )p; //ok
)
)
$(DDOC_BLANKLINE )
$(TROW cast+mutate,
$(D_CODE $(D_COMMENT // No:
)$(D_COMMENT // ptr to const int
)$(D_KEYWORD const)($(D_KEYWORD int))* p;
$(D_KEYWORD int)* q = $(D_KEYWORD cast)($(D_KEYWORD int)*)p;
*q = 3; $(D_COMMENT // undefined behavior
))
,
$(CPPLISTING // Yes:
// ptr to const int
const int* p;
int* q = const_cast$(LT )int*$(GT )p;
*q = 3; // ok
)
)
$(DDOC_BLANKLINE )
$(TROW overloading,
$(D_CODE $(D_COMMENT // Yes:
)$(D_KEYWORD void) foo($(D_KEYWORD int) x);
$(D_KEYWORD void) foo($(D_KEYWORD const) $(D_KEYWORD int) x); $(D_COMMENT //ok
))
,
$(CPPLISTING // No:
void foo(int x);
void foo(const int x); //error
)
)
$(DDOC_BLANKLINE )
$(TROW const/mutable aliasing,
$(D_CODE $(D_COMMENT // Yes:
)$(D_KEYWORD void) foo($(D_KEYWORD const) $(D_KEYWORD int)* x, $(D_KEYWORD int)* y)
{
bar(*x); $(D_COMMENT // bar$(LPAREN)3$(RPAREN )
) *y = 4;
bar(*x); $(D_COMMENT // bar$(LPAREN)4$(RPAREN )
)}
...
$(D_KEYWORD int) i = 3;
foo(&i, &i);
)
,
$(CPPLISTING // Yes:
void foo(const int* x, int* y)
{
bar(*x); // bar(3)
*y = 4;
bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
)
)
$(DDOC_BLANKLINE )
$(TROW immutable/mutable aliasing,
$(D_CODE $(D_COMMENT // No:
)$(D_KEYWORD void) foo($(D_KEYWORD immutable) $(D_KEYWORD int)* x, $(D_KEYWORD int)* y)
{
bar(*x); $(D_COMMENT // bar$(LPAREN)3$(RPAREN )
) *y = 4; $(D_COMMENT // undefined behavior
) bar(*x); $(D_COMMENT // bar$(LPAREN)??$(RPAREN )
)}
...
$(D_KEYWORD int) i = 3;
foo($(D_KEYWORD cast)($(D_KEYWORD immutable))&i, &i);
)
,
No immutables
)
$(DDOC_BLANKLINE )
$(TROW type of string literal,
$(D immutable(char)[]),
$(D const char*)
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(TROW string literal to non-const,
not allowed,
$(ARGS allowed, but deprecated)
)
)
)
$(DDOC_BLANKLINE )
$(SPEC_SUBNAV_PREV_NEXT interfaceToC, Interfacing to C, objc_interface, Interfacing to Objective-C)
)
)