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 -lcriará 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)para dar linkagem C.
{
/* ...conteúdo do arquivo... */
}
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:
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 1podem ser substituídas por:
#define BAR 2
#define ABC 3
#define DEF 40
enumou por:
{ FOO = 1,
BAR = 2,
ABC = 3,
DEF = 40
}
const int FOO = 1;Macros de funções, como:
const int BAR = 2;
const int ABC = 3;
const int DEF = 40;
#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)A versão D correspondente seria:
#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)
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 { };Pode ser substituído por dois módulos D, foo.d:
struct Foo bar;
struct Foo { }e fooextern.d:
import fooextern;
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 Foopor:
{ int a;
int b;
} Foo, *pFoo, *lpFoo;
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)se torna:
struct Foo
{
int a;
int b;
};
#pragma pack()
struct Foo
{
align (1):
int a;
int b;
}
Estruturas Aninhadas
struct Foose torna:
{
int a;
struct Bar
{
int c;
} bar;
};
struct Abc
{
int a;
struct
{
int c;
} bar;
};
struct Foo
{
int a;
struct Bar
{
int c;
}
Bar bar;
};
struct Abc
{
int a;
struct
{
int c;
}
};
__cdecl, __pascal, __stdcall
int __cdecl x;se torna:
int __cdecl foo(int a);
int __pascal bar(int b);
int __stdcall abc(int c);
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);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.
int myfoo(int a)
{
return foo(int a);
}