D Programming Language 1.0

Last update Sun Dec 30 20:34:43 2012

# Converting C `.h` Files to D Modules

While D cannot directly compile C source code, it can easily interface to C code, be linked with C object files, and call C functions in DLLs. The interface to C code is normally found in C `.h` files. So, the trick to connecting with C code is in converting C `.h` files to D modules. This turns out to be difficult to do mechanically since inevitably some human judgement must be applied. This is a guide to doing such conversions.

#### Preprocessor

`.h` files can sometimes be a bewildering morass of layers of macros, `#include` files, `#ifdef`'s, etc. D doesn't include a text preprocessor like the C preprocessor, so the first step is to remove the need for it by taking the preprocessed output. For DMC (the Digital Mars C/C++ compiler), the command:

    dmc -c program.h -e -l

will create a file `program.lst` which is the source file after all text preprocessing.

Remove all the `#if`, `#ifdef`, `#include`, etc. statements.

#### Linkage

Generally, surround the entire module with:

    extern (C)
    {
         /* ...file contents... */
    }

to give it C linkage.

#### Types

A little global search and replace will take care of renaming the C types to D types. The following table shows a typical mapping for 32 bit C code:

Mapping C type to D type
C type | D type
--- | ---
long double | real
unsigned long long | ulong
long long | long
unsigned long | uint
long | int
unsigned | uint
unsigned short | ushort
signed char | byte
unsigned char | ubyte
wchar_t | wchar or dchar
bool | bool, byte, int
size_t | size_t
ptrdiff_t | ptrdiff_t

#### NULL

`NULL` and `((void*)0)` should be replaced with `null`.

#### Numeric Literals

Any ‘L’ or ‘l’ numeric literal suffixes should be removed, as a C `long` is (usually) the same size as a D `int`. Similarly, ‘LL’ suffixes should be replaced with a single ‘L’. Any ‘u’ suffix will work the same in D.

#### String Literals

In most cases, any ‘L’ prefix to a string can just be dropped, as D will implicitly convert strings to wide characters if necessary. However, one can also replace:

    L"string"

with:

    "string"w	// for 16 bit wide characters
    "string"d	// for 32 bit wide characters

#### Macros

Lists of macros like:

    #define FOO	1
    #define BAR	2
    #define ABC	3
    #define DEF	40

can be replaced with:

    enum
    {   FOO = 1,
        BAR = 2,
        ABC = 3,
        DEF = 40
    }

or with:

    const int FOO = 1;
    const int BAR = 2;
    const int ABC = 3;
    const int DEF = 40;

Function style macros, such as:

    #define MAX(a,b) ((a) < (b) ? (b) : (a))

can be replaced with functions:

    int MAX(int a, int b) { return (a < b) ? b : a; }

The functions, however, won't work if they appear inside static initializers that must be evaluated at compile time rather than runtime. To do it at compile time, a template can be used:

    #define GT_DEPTH_SHIFT  (0)
    #define GT_SIZE_SHIFT   (8)
    #define GT_SCHEME_SHIFT (24)
    #define GT_DEPTH_MASK   (0xffU << GT_DEPTH_SHIFT)
    #define GT_TEXT         ((0x01) << GT_SCHEME_SHIFT)

    /* Macro that constructs a graphtype */
    #define GT_CONSTRUCT(depth,scheme,size) \
    	((depth) | (scheme) | ((size) << GT_SIZE_SHIFT))

    /* Common graphtypes */
    #define GT_TEXT16  GT_CONSTRUCT(4, GT_TEXT, 16)

The corresponding D version would be:

    const uint GT_DEPTH_SHIFT  = 0;
    const uint GT_SIZE_SHIFT   = 8;
    const uint GT_SCHEME_SHIFT = 24;
    const uint GT_DEPTH_MASK   = 0xffU << GT_DEPTH_SHIFT;
    const uint GT_TEXT         = 0x01 << GT_SCHEME_SHIFT;

    // Template that constructs a graphtype
    template GT_CONSTRUCT(uint depth, uint scheme, uint size)
    {
     // notice the name of the const is the same as that of the template
     const uint GT_CONSTRUCT = (depth | scheme | (size << GT_SIZE_SHIFT));
    }

    // Common graphtypes
    const uint GT_TEXT16 = GT_CONSTRUCT!(4, GT_TEXT, 16);

#### Declaration Lists

D doesn't allow declaration lists to change the type. Hence:

    int *p, q, t[3], *s;

should be written as:

    int* p, s;
    int q;
    int[3] t;

#### Void Parameter Lists

Functions that take no parameters:

    int foo(void);

are in D:

    int foo();

#### Const Type Modifiers

D has `const` as a storage class, not a type modifier. Hence, just drop any `const` used as a type modifier:

    void foo(const int *p, char *const q);

becomes:

    void foo(int* p, char* q);

#### Extern Global C Variables

Whenever a global variable is declared in D, it is also defined. But if it's also defined by the C object file being linked in, there will be a multiple definition error. To fix this problem, use the extern storage class. For example, given a C header file named `foo.h`:

    struct Foo { };
    struct Foo bar;

It can be replaced with the D modules, `foo.d`:

    struct Foo { }
    extern (C)
    {
        extern Foo bar;
    }

#### Typedef

`alias` is the D equivalent to the C `typedef`:

    typedef int foo;

becomes:

    alias int foo;

#### Structs

Replace declarations like:

    typedef struct Foo
    {   int a;
        int b;
    } Foo, *pFoo, *lpFoo;

with:

    struct Foo
    {   int a;
        int b;
    }
    alias Foo* pFoo, lpFoo;

#### Struct Member Alignment

A good D implementation by default will align struct members the same way as the C compiler it was designed to work with. But if the `.h` file has some `#pragma`'s to control alignment, they can be duplicated with the D `align` attribute:

    #pragma pack(1)
    struct Foo
    {
        int a;
        int b;
    };
    #pragma pack()

becomes:

    struct Foo
    {
      align (1):
        int a;
        int b;
    }

#### Nested Structs

    struct Foo
    {
        int a;
        struct Bar
        {
    	int c;
        } bar;
    };

    struct Abc
    {
        int a;
        struct
        {
    	int c;
        } bar;
    };

becomes:

    struct Foo
    {
        int a;
        struct Bar
        {
    	int c;
        }
        Bar bar;
    }

    struct Abc
    {
        int a;
        struct
        {
    	int c;
        }
    }

#### `__cdecl`, `__pascal`, `__stdcall`

    int __cdecl x;
    int __cdecl foo(int a);
    int __pascal bar(int b);
    int __stdcall abc(int c);

becomes:

    extern (C) int x;
    extern (C) int foo(int a);
    extern (Pascal) int bar(int b);
    extern (Windows) int abc(int c);

#### `__declspec(dllimport)`

    __declspec(dllimport) int __stdcall foo(int a);

becomes:

    export extern (Windows) int foo(int a);

#### `__fastcall`

Unfortunately, D doesn't support the `__fastcall` convention. Therefore, a shim will be needed, either written in C:

    int __fastcall foo(int a);

    int myfoo(int a)
    {
        return foo(int a);
    }

and compiled with a C compiler that supports `__fastcall` and linked in, or compile the above, disassemble it with obj2asm and insert it in a D `myfoo` shim with inline assembler.
Forums | Comments | D | Search | Downloads | Home
Copyright © 1999-2012 by Digital Mars ®, All Rights Reserved | Page generated by Ddoc.