Ddoc $(D_S Converting C $(TT .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 $(TT .h) files. So, the trick to connecting with C code is in converting C $(TT .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

$(TT .h) files can sometimes be a bewildering morass of layers of macros, $(TT #include) files, $(TT #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: $(CONSOLE dmc -c program.h -e -l ) will create a file $(TT program.lst) which is the source file after all text preprocessing.

Remove all the $(TT #if), $(TT #ifdef), $(TT #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:

$(TABLE1 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

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

Numeric Literals

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

String Literals

In most cases, any $(SINGLEQUOTE 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: $(CCODE L"string" ) with: --------------------------- "string"w // for 16 bit wide characters "string"d // for 32 bit wide characters ---------------------------

Macros

Lists of macros like: $(CCODE #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: $(CCODE #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: $(CCODE #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: $(CCODE 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: $(CCODE int foo(void); ) are in D: --------------------------- int foo(); --------------------------- $(V1

Const Type Modifiers

D has $(TT const) as a storage class, not a type modifier. Hence, just drop any $(TT const) used as a type modifier: $(CCODE 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 $(TT foo.h): $(CCODE struct Foo { }; struct Foo bar; ) It can be replaced with the D modules, $(TT foo.d): --------------------------- struct Foo { } extern (C) { extern Foo bar; } ---------------------------

Typedef

$(TT alias) is the D equivalent to the C $(TT typedef): $(CCODE typedef int foo; ) becomes: --------------------------- alias int foo; ---------------------------

Structs

Replace declarations like: $(CCODE 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 $(TT .h) file has some $(TT #pragma)'s to control alignment, they can be duplicated with the D $(TT align) attribute: $(CCODE #pragma pack(1) struct Foo { int a; int b; }; #pragma pack() ) becomes: --------------------------- struct Foo { align (1): int a; int b; } ---------------------------

Nested Structs

$(CCODE 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; } } ---------------------------

$(TT __cdecl), $(TT __pascal), $(TT __stdcall)

$(CCODE 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); ---------------------------

$(TT __declspec(dllimport))

$(CCODE __declspec(dllimport) int __stdcall foo(int a); ) becomes: --------------------------- export extern (Windows) int foo(int a); ---------------------------

$(TT __fastcall)

Unfortunately, D doesn't support the $(TT __fastcall) convention. Therefore, a shim will be needed, either written in C: $(CCODE int __fastcall foo(int a); int myfoo(int a) { return foo(int a); } ) and compiled with a C compiler that supports $(TT __fastcall) and linked in, or compile the above, disassemble it with obj2asm and insert it in a D $(TT myfoo) shim with inline assembler. ) Macros: TITLE=Converting C .h Files to D Modules WIKI=HToModule