www.digitalmars.com
Last update Tue Jun 6 16:38:20 2006

Interfacing to C

D is designed to fit comfortably with a C compiler for the target system. D makes up for not having its own VM by relying on the target environment's C runtime library. It would be senseless to attempt to port to D or write D wrappers for the vast array of C APIs available. How much easier it is to just call them directly.

This is done by matching the C compiler's data types, layouts, and function call/return sequences.

Calling C Functions

C functions can be called directly from D. There is no need for wrapper functions, argument swizzling, and the C functions do not need to be put into a separate DLL.

The C function must be declared and given a calling convention, most likely the "C" calling convention, for example:

extern (C) int strcmp(char* string1, char* string2);
and then it can be called within D code in the obvious way:
import std.string;
int myDfunction(char[] s)
{
    return strcmp(std.string.toStringz(s), "foo");
}
There are several things going on here: C code can correspondingly call D functions, if the D functions use an attribute that is compatible with the C compiler, most likely the extern (C):
// myfunc() can be called from any C function
extern (C)
{
    void myfunc(int a, int b)
    {
	...
    }
}

Storage Allocation

C code explicitly manages memory with calls to malloc() and free(). D allocates memory using the D garbage collector, so no explicit free's are necessary.

D can still explicitly allocate memory using c.stdlib.malloc() and 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 type C type
void void
bit no equivalent
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 float _Imaginary
idouble double _Imaginary
ireal long double _Imaginary
cfloat float _Complex
cdouble double _Complex
creal long double _Complex
struct struct
union union
enum enum
class no equivalent
type* 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.

Calling printf()

This mostly means checking that the printf format specifier matches the corresponding D data type. Although printf is designed to handle 0 terminated strings, not D dynamic arrays of chars, it turns out that since D dynamic arrays are a length followed by a pointer to the data, the %.*s format works perfectly:
void foo(char[] string)
{
    printf("my string is: %.*s\n", string);
}
The printf format string literal in the example doesn't end with \0. This is because string literals, when they are not part of an initializer to a larger data structure, have a \0 character helpfully stored after the end of them.

An improved D function for formatted output is std.stdio.writef().

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.


Interfacing to C++

D does not provide an interface to C++. Since D, however, interfaces directly to C, it can interface directly to C++ code if it is declared as having C linkage.

D class objects are incompatible with C++ class objects.