www.digitalmars.com Home | Search | D | Comments
Last update Fri Aug 26 2005
D
Language
Phobos
Comparisons

· Overview
· D for Win32
· Win32 DLLs in D
· C .h to D Modules
· FAQ
· Style Guide
· Example: wc
· Future
· D Change Log
· Tech Tips
· Glossary
· Acknowledgements

Tools
· DMD D Compiler
· GDC D Compiler
· Linker
· Profiler

Community
· News Digest
· News
· Forum
· Announcements
· Learn
· D links

Archives
· digitalmars.D
· digitalmars.D.dtl
· digitalmars.D.announce
· digitalmars.D.learn
· digitalmars.D.bugs
· D.gnu
· Old D

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:
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 int

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:
	cast(wchar[])"string"
	

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); }
	

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, the idea is to declare the variable in a D module, but not link in that module. For example, given a C header file named foo.h:
	struct Foo { };
	struct Foo bar;
	
It can be replaced with two D modules, foo.d:
	struct Foo { }
	import fooextern;
	
and fooextern.d:
	Foo bar;
	
The foo.obj file is linked in, and fooextern.obj is not. While this is not the most elegant looking method, it does work, and since it is pretty rare in C libraries to use global variables in its interface, it isn't an issue in practice.

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.

Feedback and Comments

Add feedback and comments regarding this page.