Last update Feb 10, 2005
This is done by matching the C compiler's data types, layouts, and function call/return sequences.
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:
// myfunc() can be called from any C function extern (C) { void myfunc(int a, int b) { ... } }
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:
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.
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.
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().
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.
D class objects are incompatible with C++ class objects.