D имеет фантастическую модульную систему, которая значительно сокращает время компиляции по сравнению с С++. Согласно документации D все еще содержит непрозрачные структуры и союзы, чтобы позволить идиому pimpl. Мой вопрос: как я могу объявить вложенную структуру (или объединение) в одном модуле и определить ее в другой? Каков синтаксис для этого?
В С++ заголовок будет выглядеть следующим образом:
struct S {
...
struct Impl;
Impl * p;
};
и файл реализации (cpp файл) будет использовать интересный ::
-syntax, как это:
#include "header.h"
struct S::Impl {
...
};
Как реализовать то же самое в D?
Ответ 1
D (как минимум, DMD) использует .di
файлы для деклараций. Они несколько эквивалентны файлам C .h
, однако они являются необязательными. Компилятор D может автоматически генерировать файлы .di
(когда указан параметр -H
), хотя я считаю, что в настоящее время все, что он делает, это тело функции полосы и unittests.
Здесь один из способов достижения PImpl с использованием файлов .di
:
Обратите внимание, что в настоящее время вы несете ответственность за то, чтобы поля в S
были одинаковыми в файлах .d
и .di
- если они отличаются, скомпилированные модули будут иметь разные знания о том, как поля, которые могут привести к повреждению памяти. Текущие реализации компилятора не проверяют соответствие определений в файлах .d
и .di
.
Ответ 2
Мой вопрос: как я могу объявить вложенную структуру (или объединение) в одном модуле и определить ее в другом?
Чтобы получить это прямо - это намеренно невозможно в D по дизайну. Это прямое следствие наличия надежной модульной системы - каждое объявление символа неявно квалифицируется по имени модуля, которое оно объявлено внутри. И по целому ряду причин вы не можете захватить символ в другие "пространство имен" модулей.
Тем не менее, нет необходимости делать это в том же модуле, чтобы использовать подход pimpl. Вы можете обратиться к CyberShadow для получения более подробной информации.
Ответ 3
Другой подход основан на системе D иерархии классов:
все объекты наследуют явно или неявно Object.
Итак, идея состоит в том, чтобы реализовать OuterClass с помощью pimpl, сгенерировать соответствующие
di-file, вручную удалите все определения OuterClassPrivate из di файла
и изменение объявления элемента pimpl.
Например:
первая версия разделяемой библиотеки
module pimpl.mylib;
class PimplTest
{
this()
{
mImpl = new PimplTestPrivate();
}
~this()
{
}
string sayWhat(string what)
{
return mImpl.ku ~ " " ~ what;
}
private class PimplTestPrivate
{
string ku = "Ku!!1";
}
private PimplTestPrivate mImpl;
}
тестовое приложение:
module main;
import std.stdio;
import pimpl.mylib;
void main()
{
PimplTest t = new PimplTest();
writeln(t.sayWhat("?"));
}
Общий mylib может быть построен следующим образом (под Linux):
$ dmd -H -c mylib.d -fPIC
$ dmd -ofmylib.so mylib.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/phobos/library/is
Затем отредактируйте файл в формате genereated di-file:
// D import file generated from 'mylib.d'
module pimpl.mylib;
class PimplTest
{
this();
~this();
string sayWhat(string what);
// NOTE this
private Object mImpl;
}
Скомпилируйте тест
$ dmd -c main.d /path/to/first/version/of/mylib.di
$ ln -s /path/to/first/version/of/mylib.so .
$ dmd main.o -L-l:mylib.so -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/phobos/library/is:.
$ ./main
Say: ?
Затем мы меняем mylib:
module pimpl.mylib;
import std.conv;
class PimplTest
{
this()
{
mImpl = new PimplTestPrivate();
}
~this()
{
}
string sayWhat(string what)
{
return mImpl.getMessage1(mImpl.getValue(), what);
}
private class PimplTestPrivate
{
int getValue()
{
return 42;
}
string ku = "Ku!!1";
string getMessage1(int x, string y)
{
return "x = " ~ to!(string)(x) ~ ", " ~ y;
}
double pi = 22.0/7.0;
}
private PimplTestPrivate mImpl;
}
Скомпилируйте его и замените двоичный общий объект (так файл) первой версии mylib только что построенным. Запуск тестового приложения не должен вылетать, но результат будет другим.