· 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 ModulesWhile 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 -lwill create a file program.lst which is the source file after all text preprocessing. Remove all the #if, #ifdef, #include, etc. statements. LinkageGenerally, surround the entire module with:extern (C) { ...file contents... }to give it C linkage. TypesA 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:
NULLNULL and ((void*)0) should be replaced with null.Numeric LiteralsAny '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 LiteralsIn 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" MacrosLists of macros like:#define FOO 1 #define BAR 2 #define ABC 3 #define DEF 40can 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 ListsD 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 ListsFunctions that take no parameters:int foo(void);are in D: int foo(); Const Type ModifiersD 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 VariablesWhenever 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. Typedefalias is the D equivalent to the C typedef:typedef int foo;becomes: alias int foo; StructsReplace 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 AlignmentA 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 Structsstruct 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, __stdcallint __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); __fastcallUnfortunately, 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. |
Add feedback and comments regarding this page.