- PVSM.RU - https://www.pvsm.ru -
Уолтер Брайт [1] — «великодушный пожизненный диктатор» языка программирования D и основатель Digital Mars [2]. За его плечами не один десяток лет опыта в разработке компиляторов и интерпретаторов для нескольких языков, в числе которых Zortech C++ — первый нативный компилятор C++. Он также создатель игры Empire [3], послужившей основным источником вдохновения для Sid Meier’s Civilization.
Better C — это способ перенести существующие проекты на языке C на язык D в последовательной манере. В этой статье показан пошаговый процесс конвертации нетривиального проекта из C в D и показывает частые проблемы, которые при этом возникают.
Хотя фронтенд компилятора D dmd [6] уже сконвертирован в D, это настолько большой проект, что его трудно целиком охватить. Мне был нужен более мелкий и скромный проект, который можно было бы полностью уяснить, но чтобы он не был умозрительным примером.
Мне пришла на ум старая make-программа, которую я написал для компилятора C Datalight [7] в начале 1980-х. Это реальная имплементация классической программы make, которая постоянно использовалась с начала 80-х. Она написана на C ещё до его стандартизации, была портирована из одной системы в другую и укладывается всего в 1961 строчку кода, включая комментарии. Она до сих пор регулярно используется.
Вот документация [8] и исходный код [9]. Размер исполняемого файла make.exe — 49 692 байта, и последнее изменение было 19 августа 2012 г.
Наш Злобный план:
Только когда мы закончим, можно будет приступить к исправлениям, рефакторингу, подчистке и т.д.
Законченный перенос с C на D [10]. Размер исполняемого файла — 52 252 байт (сравнимо с оригиналом — 49 692 байт). Я не анализировал увеличение в размере, но вероятно, оно взялось из-за экземпляров шаблона NEWOBJ (в C-версии это макрос) и изменений в рантайме DMC после 2012 года.
Вот отличия между версиями [11]. Изменились 664 строки из 1961, примерно треть — кажется, что много, но надеюсь, я смогу вас убедить, что почти все отличия тривиальны.
Включение файлов через #include заменено на импортирование соответствующих модулей D: например, #include <stdio.h> заменено на import core.stdc.stdio;. К сожалению, некоторые из включаемых файлов специфичны для Digital Mars C, и для них не существует версий на D (это надо исправить). Чтобы не останавливаться на этом, я просто вставил соответствующие объявления с 29-й строки по 64-ю [12]. (См. документацию по объявлению import [13]).
Конструкция #if _WIN32 заменена [14] на version (Windows) [15]. (См. документацию по условной компиляции версий [16] и список предопределённых версий [17]).
Объявление extern(C): [18] помечает последующие объявления в файле как совместимые с C. (См. документацию по аттрибуту линковки [19]).
При помощи глобального поиска и замены макросы debug1, debug2 и debug3 [20] заменены на debug prinf [21]. В целом, директивы препроцессора #ifdef DEBUG [22] заменены на условную компиляцию при помощи debug [23]. (См. документацию по выражению debug [24]).
/* Delete these old C macro definitions...
#ifdef DEBUG
-#define debug1(a) printf(a)
-#define debug2(a,b) printf(a,b)
-#define debug3(a,b,c) printf(a,b,c)
-#else
-#define debug1(a)
-#define debug2(a,b)
-#define debug3(a,b,c)
-#endif
*/
// And replace their usage with the debug statement
// debug2("Returning x%lxn",datetime);
debug printf("Returning x%lxn",datetime);
Макросы TRUE, FALSE и NULL [25] при помощи поиска и замены заменены на true, false и null.
Макрос ESC [26] заменён константой времени компиляции [27]. (См. документацию по константам [28]).
// #define ESC '!'
enum ESC = '!';
Макрос NEWOBJ [29] заменён шаблонной функцией [30].
// #define NEWOBJ(type) ((type *) mem_calloc(sizeof(type)))
type* NEWOBJ(type)() { return cast(type*) mem_calloc(type.sizeof); }
Макрос filenamecmp [31] заменён функцией [32].
Убрана поддержка устаревших платформ [33].
Глобальные переменные в D по умолчанию помещаются в локальное хранилище потока (thread-local storage, TLS). Но поскольку make — однопоточная программа, их можно поместить в глобальное хранилище при помощи класса хранилища __gshared [34]. (См. документацию по атрибуту __gshared [35]).
// int CMDLINELEN;
__gshared int CMDLINELEN
В D нет отдельного пространства имён для структур, так что в typedef нет необходимости. Вместо этого можно использовать alias [36]. (См. документацию по объявлению alias [37]). Кроме того, из объявлений переменных убрано слово struct [38].
/*
typedef struct FILENODE
{ char *name,genext[EXTMAX+1];
char dblcln;
char expanding;
time_t time;
filelist *dep;
struct RULE *frule;
struct FILENODE *next;
} filenode;
*/
struct FILENODE
{
char *name;
char[EXTMAX1] genext;
char dblcln;
char expanding;
time_t time;
filelist *dep;
RULE *frule;
FILENODE *next;
}
alias filenode = FILENODE;
В языке D macro [39] — это ключевое слово, поэтому вместо этого будем использовать MACRO.
В отличие от C, в языке D звёздочка в объявлении указателя является частью типа, поэтому при объявлении нескольких указателей [40] звёздочка применяется к каждому символу [41]:
// char *name,*text;
// In D, the * is part of the type and
// applies to each symbol in the declaration.
char* name, text;
Объявления массивов в стиле C [42] преобразованы в объявления в стиле D [38]. (См. документацию по синтаксису объявлений в D [43]).
Слово static [44] на уровне модуля в D ничего не значит. В C статические глобальные переменные эквивалентны приватным переменным уровня модуля в D, но это неважно, если модуль никогда не импортируется. Их всё ещё нужно обозначить как __gshared, и этот можно сделать целым блоком [45]. (См. документацию по атрибуту static [46]).
/*
static ignore_errors = FALSE;
static execute = TRUE;
static gag = FALSE;
static touchem = FALSE;
static debug = FALSE;
static list_lines = FALSE;
static usebuiltin = TRUE;
static print = FALSE;
...
*/
__gshared
{
bool ignore_errors = false;
bool execute = true;
bool gag = false;
bool touchem = false;
bool xdebug = false;
bool list_lines = false;
bool usebuiltin = true;
bool print = false;
...
}
Предварительные объявления функций [47] в языке D не нужны. Функцию, определённую на уровне модуля, можно вызывать из любого места в этом модуле, даже до её определения.
В расширении символов подстановки [48] в make-программе нет большого смысла.
Параметры функций, определённые с синтаксисом массивов [49], на самом деле являются указателями, и в D объявляются как указатели.
// int cdecl main(int argc,char *argv[])
int main(int argc,char** argv)
Макрос mem_init() ни во что не расширяется [50], и до этого мы его убрали.
В C можно грязно играть с аргументами [51], но D требует, чтобы они соответствовали прототипу функции.
void cmderr(const char* format, const char* arg) {...}
// cmderr("can't expand response filen");
cmderr("can't expand response filen", null);
При помощи глобального поиска и замены оператор-стрелка (->) языка C [52] заменён на точку (.), поскольку в D доступ к членам осуществляется одинаково.
Директивы условной компиляции [53] заменены на version.
/*
#if TERMCODE
...
#endif
*/
version (TERMCODE)
{
...
}
Отсутствие прототипов функций [54] свидетельствует о древности этого кода. D требует полноценных прототипов.
// doswitch(p)
// char *p;
void doswitch(char* p)
В языке D слово debug зарезервировано [55]. Переименуем в xdebug.
Многострочные литералы в C требуют n в конце каждой строки [56]. В D этого не требуется.
Неиспользуемый код закомментирован [57] при помощи вложенного блока комментариев /+ +/. (См. документацию по строчным, блочным и вложенным комментариям [58]).
Выражение static if может во многих случаях заменить #if [59]. (См. документацию по static if [60]).
Массивы в D не сводятся к указателю автоматически [61], следует использовать .ptr.
// utime(name,timep);
utime(name,timep.ptr);
Использование const для строк в стиле C [62] проистекает из строковых литералов в D, поскольку D не позволяет брать изменяемые указатели на строковые литералы. (См. документацию по const и immutable [63]).
// linelist **readmakefile(char *makefile,linelist **rl)
linelist **readmakefile(const char *makefile,linelist **rl)
Преобразование void* в char* [64] в D должно быть явным.
// buf = mem_realloc(buf,bufmax);
buf = cast(char*)mem_realloc(buf,bufmax);
Тип unsigned заменён на uint [65].
Атрибут inout можно использовать [66], чтобы передать «константность» аргумента функции на возвращаемый тип. Если параметр обозначен как const, то таким же будет возвращаемое значение, и наоборот. (См. документацию по inout-функциям [67]).
// char *skipspace(p) {...}
inout(char) *skipspace(inout(char)* p) {...}
Макрос arraysize [68] можно заменить на свойство .length. (См. документацию по свойствам массивов [69]).
// useCOMMAND |= inarray(p,builtin,arraysize(builtin));
useCOMMAND |= inarray(p,builtin.ptr,builtin.length)
Строковые литералы неизменяемы (immutable), поэтому изменяемые строки необходимо заменить на массивы, выделенные на стеке [70]. (См. документацию по строковым литералам [71]).
// static char envname[] = "@_CMDLINE";
char[10] envname = "@_CMDLINE";
Свойство .sizeof служит заменой оператору sizeof() из C [72]. (См. документацию по .sizeof [73]).
// q = (char *) mem_calloc(sizeof(envname) + len);
q = cast(char *) mem_calloc(envname.sizeof + len)
Старые версии Windows [74] нас не интересуют.
Доисторическое применение char * заменено на void* [75].
И вот и все изменения! Как видите, не так уж плохо. Я не выставлял таймер, но сомневаюсь, что всё это заняло у меня больше часа — включая исправление нескольких ошибок, которые я сделал в процессе.
У нас остаётся только файл man.c [76], который был нужен, чтобы открывать в браузере документацию по make [8] при запуске с опцией -man. К счастью, он уже портирован на D, так что я могу просто скопировать код [77].
Собрать make так просто, что для этого даже не требуется make-файл:
dmd2.079windowsbindmd make.d dman.d -O -release -betterC -I. -Idmd2.079srcdruntimeimport shell32.lib
Мы придерживались нашего Злобного плана по портированию нетривильной программы на олдскульном C на язык D, и смогли сделать это быстро и корректно. Мы получили эквивалентный исполняемый файл.
Проблемы, с которыми мы столкнулись, типичны и легко решаются следующими способами:
#include на import;->;Не потребовалось ничего из следующего:
Теперь, когда у нас есть Better C, нам доступны многие современные возможности, которые позволят нам улучшить наш код:
Если вы знаете английский, заходите на форум D [88] и расскажите нам, как продвигается ваш проект на Better C!
Автор: Ogi
Источник [89]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/dlang/355239
Ссылки в тексте:
[1] Уолтер Брайт: http://walterbright.com/
[2] Digital Mars: http://digitalmars.com/
[3] Empire: http://www.classicempire.com/
[4] D как улучшенный C: https://habr.com/ru/post/511334/
[5] Баги, которые разрушили ваш замок: https://habr.com/ru/post/511560/
[6] фронтенд компилятора D dmd: https://github.com/dlang/dmd
[7] Datalight: https://en.wikipedia.org/wiki/Datalight
[8] документация: https://digitalmars.com/ctg/make.html
[9] исходный код: https://github.com/DigitalMars/Compiler/commit/473bf5bef99a1748d5154b343b986534271cd841#diff-14c44b49b9f46b1aeab33a6684214e55
[10] Законченный перенос с C на D: https://github.com/DigitalMars/Compiler/blob/1dbd3e3381c8f7f7ab2e35214dcd63455ae38c29/dm/src/make/dmake.d
[11] отличия между версиями: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34
[12] вставил соответствующие объявления с 29-й строки по 64-ю: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R29
[13] объявлению import: https://dlang.org/spec/module.html#import-declaration
[14] #if _WIN32 заменена: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L23
[15] version (Windows): https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R20
[16] условной компиляции версий: https://dlang.org/spec/version.html#version
[17] список предопределённых версий: https://dlang.org/spec/version.html#predefined-versions
[18] extern(C):: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R27
[19] аттрибуту линковки: https://dlang.org/spec/attribute.html#linkage
[20] макросы debug1, debug2 и debug3: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L27
[21] debug prinf: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R499
[22] директивы препроцессора #ifdef DEBUG: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff%E2%80%9314c44b49b9f46b1aeab33a6684214e55L290
[23] условную компиляцию при помощи debug: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R276
[24] документацию по выражению debug: https://dlang.org/spec/version.html#DebugStatement
[25] TRUE, FALSE и NULL: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L37
[26] Макрос ESC: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L40
[27] константой времени компиляции: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R66
[28] константам: https://dlang.org/spec/enum.html#manifest_constants
[29] Макрос NEWOBJ: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L42
[30] шаблонной функцией: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R68
[31] Макрос filenamecmp: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L56
[32] функцией: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R74
[33] Убрана поддержка устаревших платформ: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L45
[34] класса хранилища __gshared: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R82
[35] атрибуту __gshared: https://dlang.org/spec/attribute.html#gshared
[36] alias: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R113
[37] объявлению alias: https://dlang.org/spec/declaration.html#alias
[38] struct: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R131
[39] macro: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L86
[40] при объявлении нескольких указателей: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L83
[41] к каждому символу: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R98
[42] Объявления массивов в стиле C: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L111
[43] синтаксису объявлений в D: https://dlang.org/spec/declaration.html#declaration_syntax
[44] Слово static: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L159
[45] сделать целым блоком: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R191
[46] атрибуту static: https://dlang.org/spec/attribute.html#static
[47] Предварительные объявления функций: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L189
[48] расширении символов подстановки: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L240
[49] Параметры функций, определённые с синтаксисом массивов: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L242
[50] Макрос mem_init() ни во что не расширяется: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L248
[51] аргументами: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L275
[52] оператор-стрелка (->) языка C: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L297
[53] Директивы условной компиляции: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L303
[54] Отсутствие прототипов функций: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R312
[55] слово debug зарезервировано: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L335
[56] n в конце каждой строки: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L396
[57] Неиспользуемый код закомментирован: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R463
[58] строчным, блочным и вложенным комментариям: https://dlang.org/spec/lex.html#comment
[59] заменить #if: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R503
[60] static if: https://dlang.org/spec/version.html#staticif
[61] не сводятся к указателю автоматически: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L585
[62] Использование const для строк в стиле C: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L658
[63] const и immutable: https://dlang.org/spec/const3.html#const_and_immutable
[64] Преобразование void* в char*: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R920
[65] Тип unsigned заменён на uint: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L1099
[66] можно использовать: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R1160
[67] inout-функциям: https://dlang.org/spec/function.html#inout-functions
[68] Макрос arraysize: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L1628
[69] свойствам массивов: https://dlang.org/spec/arrays.html#array-properties
[70] изменяемые строки необходимо заменить на массивы, выделенные на стеке: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L1683
[71] строковым литералам: https://dlang.org/spec/lex.html#string_literals
[72] служит заменой оператору sizeof() из C: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L1688
[73] .sizeof: https://dlang.org/spec/property.html#sizeof
[74] Старые версии Windows: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55R1692
[75] заменено на void*: https://github.com/DigitalMars/Compiler/commit/a25bee1ba3a3b5231fd80aa1b24aa18ce3eb3a34#diff-14c44b49b9f46b1aeab33a6684214e55L1773
[76] man.c: https://github.com/DigitalMars/Compiler/commit/473bf5bef99a1748d5154b343b986534271cd841#diff-69c1b24ec5df3f3b5a5bb99bc60345f7
[77] скопировать код: https://github.com/DigitalMars/Compiler/commit/7765879be1be6fcb3eb959d1598d8ff227c2b0af#diff-2bac52ea58e12548536bb246cced3af4
[78] модули: https://dlang.org/spec/module.html
[79] проверку переполнения буфера: https://dlang.org/spec/arrays.html#bounds
[80] RAII: https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
[81] вложенные функции: https://dlang.org/spec/function.html#nested
[82] методы: https://dlang.org/spec/class.html#member-functions
[83] перегрузка операторов: https://dlang.org/spec/operatoroverloading.html
[84] генератор документации: https://dlang.org/spec/ddoc.html
[85] функциональное программирование: https://dlang.org/spec/function.html#pure-functions
[86] выполнение функций во время компиляции (CTFE): https://dlang.org/spec/function.html#interpretation
[87] и многое другое: https://dlang.org/spec/spec.html
[88] форум D: https://forum.dlang.org/group/general
[89] Источник: https://habr.com/ru/post/511806/?utm_source=habrahabr&utm_medium=rss&utm_campaign=511806
Нажмите здесь для печати.