www.digitalmars.com
Last update Sun Aug 20 17:04:33 2006

Convertendo Arquivos C .h para Módulos D

Enquanto D não é possível compilar código fonte C, pode facilmente se conectar a código C, ser linkando com arquivos objeto C e chamar funções C em DLLs. A interface para código C normalmente é encontrada em arquivos C .h. Então, o truque para se conectar com código C está em converter arquivos C .h para módulos D. Isso é difícil de fazer manualmente já que inevitavelmente alguns julgamentos humanos devem ser aplicados. Este é um guia para fazer tais conversões.

O pré-processador

Arquivos .h files podem algumas vezes ser confusos aglomerados de camadas de macros, arquivos #include, #ifdef's, etc. D não inclui um pré-processador como o do C, então o primeiro passo é remover a necessidade disso pegando a saída do texte pré-processado. Para o DMC (o compilador C/C++ da Digital Mars), o comando:
dmc -c programa.h -e -l
criará um arquivo programa.lst que é o arquivo fonte após todo pré-processamento do texto.

Remova todas as instruções #if, #ifdef, #include, etc..

Linkagem

Normalmente, envolva todo o módulo com:
extern (C)
{
/* ...conteúdo do arquivo... */
}
para dar linkagem C.

Tipos

Uma pequena busca global e substituição cuidarão de renomear tipos C para tipos D. A seguinte tabela mostra um mapeamento típico para código C de 32 bits:

Mapeando tipo C em tipo D
tipo C tipo D
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 byte or int
size_t size_t
ptrdiff_t ptrdiff_t

NULL

NULL e ((void*)0) deveriam ser substituídos por null.

Literais numéricos

Quaisquer sufixos de literais numéricos 'L' ou 'l' deveriam ser removidos, como um long do C é (normalmente) do mesmo tamanho que um int do D. Semelhantemente, sufixos 'LL' deveriam ser substituídos por um único 'L'. Qualquer sufixo 'u' trabalhará igualmente em D.

String Literals

Na maioria dos casos, qualquer prefixo 'L' para uma string pode ser retirado, como D irá converter implicitamente para caracteres largos se necessário. Porém, também pode substituir:
L"string"
por:
cast(wchar[])"string"

Macros

Listas de macros como:
#define FOO 1
#define BAR 2
#define ABC 3
#define DEF 40
podem ser substituídas por:
enum
{ FOO = 1,
BAR = 2,
ABC = 3,
DEF = 40
}
ou por:
const int FOO = 1;
const int BAR = 2;
const int ABC = 3;
const int DEF = 40;
Macros de funções, como:
#define MAX(a,b) ((a) < (b) ? (b) : (a))
podem ser substituídas por funções:
int MAX(int a, int b) { return (a < b) ? b : a; }
A função, porém, não funcionará se aparecer dentro de inicializadores estáticos que devem ser avaliados em tempo de compilação, ao invés de em tempo de execução. Para fazer isso em tempo de compilação, um gabarito pode ser usado:
#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)
A versão D correspondente seria:
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);

Listas de Declaração

D não permite a listas de declaração mudar o tipo. Portanto:
int *p, q, t[3], *s;
deveria ser escrito como:
int* p, s;
int q;
int[3] t;

Listas de Parâmetros Vazias

Funções que não recebem parâmetros:
int foo(void);
são, em D:
int foo();

Modificadores de Tipo const

D tem const como classe de armazenamento, não como um modificador de tipo. Portanto, apenas retire const's usados como modificadores de tipo:
void foo(const int *p, char *const q);
se torna:
void foo(int* p, char* q);

Variáveis Globais C Externas

Onde quer que uma variável global seja declarada em D, ela também é definida. Mas se ela também for definida pelo arquivo objeto C sendo linkado, haverá um erro de definição múltipla. Para consertar esse problema, a idéia é declarar a variável em um módulo D, mas não linkar esse módulo. Por exemplo, dado um arquivo de cabeçalho C chamado foo.h:
struct Foo { };
struct Foo bar;
Pode ser substituído por dois módulos D, foo.d:
struct Foo { }
import fooextern;
fooextern.d:
Foo bar;
O arquivo foo.obj é linkado, e fooextern.obj não. Enquanto isso não é o método mais elegante, ele funciona, e já que é raro bibliotecas C usarem variáveis globais em suas interfaces, não é um problema na prática.

Typedef

alias é o equivalente em D para o typedef do C:
typedef int foo;
se torna:
alias int foo;

Estruturas

Substitua declarações como:
typedef struct Foo
{ int a;
int b;
} Foo, *pFoo, *lpFoo;
por:
struct Foo
{ int a;
int b;
}
alias Foo* pFoo, lpFoo;

Alinhamento de Membros de Estruturas

Uma boa implementação de D por padrão alinhará membros de estruturas do mesmo jeito que o compilador C foi projetado para fazer. Mas se os arquivos .h tiverem algum #pragma para controlar o alinhamento, eles podem ser duplicados com o atributo D align:
#pragma pack(1)
struct Foo
{
int a;
int b;
};
#pragma pack()
se torna:
struct Foo
{
align (1):
int a;
int b;
}

Estruturas Aninhadas

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

struct Abc
{
int a;
struct
{
int c;
} bar;
};
se torna:
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);
se torna:
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);
se torna:
export extern (Windows) int foo(int a);

__fastcall

Infelizmente, D não suporta a convenção __fastcall. Portanto, uma gambiarra será necessária, se for escrito em C:
int __fastcall foo(int a);

int myfoo(int a)
{
return foo(int a);
}
e compilado com um compilador C que suporta __fastcall e linkado, ou compile, desmonte com obj2asm e insira em um  myfoo em D a gambiarra com inline assembler.