Idiomatic D. Подводные камни при портировании с C

Очень часто программистам приходится портировать код из одного языка программирования в другой, ведь не писать же свой «велосипед», когда можно просто «скопипастить» чужой код и немного подправить… Однако, не все так просто! Существуют различные сложности, связанные с архитектурой языков, компиляторов и «железа». Это, своего рода, «подводные камни», для преодоления которых, при портировании кода из C в D, может помочь наш очередной перевод статьи Idiomatic D.

Глобальные переменные должны быть помечены как __gshared

Переменные в глобальной области видимости находятся в локальном хранилище потоков (Thread Local Storage, TLS), если они не квалифицированы как shared или __gshared. Вы, вероятно, захотите использовать __gshared по причине неясного будущего shared.

// Глобальная переменная в C
int my_global_var;

// Эквивалент в D
__gshared int myGlobalVar;

long и unsigned long

long и unsigned long в C имеют переменный размер, ни один встроенный тип не эквивалентен D!

Рекомендуемый способ — использовать c_long и c_ulong из модуля core.stdc.config.

// Объявление функции в C
unsigned long countBeans(const long *n)

// Эквивалент в D
import core.stdc.config;
c_ulong countBeans(const(c_long)* n);

c_int и c_uint также имеются для замены int и unsigned int, но поскольку в большинстве архитектур они 32-битные, вместо этого обычно они просто переводятся с помощью int и uint.

char

В C тип char может относиться либо к signed char, либо к unsigned char, в зависимости от реализации.

В D char всегда является целым числом без знака (от 0 до 255). Если вам нужен эквивалент signed char, используйте byte.

// Объявление функции в C
unsigned char * computeBlurb(signed char *data);

// Эквивалент в D
char* computeBlurb(byte* data);

Объявление многомерных массивов

// Объявление массива в C
int myMatrix[4][2] = { { 1, 2}, { 3, 4}, { 5, 6}, { 7, 8} };

// Эквивалент в D
int[2][4] myMatrix = [ [ 1, 2], [ 3, 4], [ 5, 6], [ 7, 8] ];

Перечислимые типы без пространства имен

// Объявление enum в C
typedef enum
{
	STRATEGY_RANDOM,
	STRATEGY_IMMEDIATE,
	STRATEGY_SEARCH
} strategy_t;

// Эквивалент в D
alias strategy_t = int;
enum : strategy_t
{
	STRATEGY_RANDOM,
	STRATEGY_IMMEDIATE,
	STRATEGY_SEARCH
}

Это позволяет избежать необходимости писать Strategy_t.STRATEGY_IMMEDIATE вместо STRATEGY_IMMEDIATE при переносе кода C.

Анонимные struct и union

D предоставляет ограниченную форму анонимной вложенной структуры и объединения (union), но их нельзя использовать для перевода анонимной структуры C:

// Анонимная структура C
struct Foo
{
	struct
	{
		int x;
	} bar;
};

// Эквивалент D
struct Foo
{
	private struct bar_t
	{
		int x;
	}
	bar_t bar;
}

Явное преобразование массива в указатель

При переносе из C вам, вероятно, придется спамить .ptr везде, где массив неявно преобразуется в указатель.

// В C
void sum(const int *array, int n);
int coeff[16];
sum(coeff, sizeof(coeff) / sizeof(int));

// В D
void sum(const(int)* array, int n);
int[16] coeff;
sum(coeff.ptr, coeff.sizeof / int.sizeof); // массив неявно преобразуемый в указатель

Добавить комментарий