Ответ 1
Нет, ответа нет.:-( Я бы подумал, что почти каждый эксперт OO говорит, что "предпочитает композицию над наследованием", там будет поддержка языка для создания композиции намного проще, чем наследование.
Pimpl является источником шаблона во множестве кода на С++. Они кажутся такими, что может быть решена комбинация макросов, шаблонов и, возможно, небольшая внешняя инструментальная помощь, но я не уверен, каким будет самый простой способ. Я видел шаблоны, которые помогают сделать некоторые из них, но не очень - вам все равно придется писать функции пересылки для каждого метода класс, который вы пытаетесь обернуть. Есть ли более простой способ?
Я представляю инструмент, используемый как часть процесса make. Вы хотите, чтобы ваши публичные заголовки были классами pimpl'd, поэтому вы предоставляете некоторый входной файл, например pimpl.in, который перечисляет классы (реализованные un-pimpl'd), которые вы хотите обернуть, затем этот файл рассматривается, классы pimpl генерируются, и во время "make install" устанавливаются только их заголовки (а не исходные заголовки классов). Проблема в том, что я не вижу никакого способа сделать это без полноценного синтаксического анализатора С++, что даже производители компиляторов не могут получить права. Возможно, классы могут быть написаны каким-то образом, что облегчает работу с внешним инструментом, но я уверен, что в конечном итоге я пропущу всевозможные угловые случаи (например, шаблонные шаблоны и/или шаблонные функции-члены).
Любые идеи? У кого-нибудь еще есть решение для этой проблемы?
Нет, ответа нет.:-( Я бы подумал, что почти каждый эксперт OO говорит, что "предпочитает композицию над наследованием", там будет поддержка языка для создания композиции намного проще, чем наследование.
Я не говорю, что это хорошо (просто то, что возникло на ум).
Но вы можете экспериментировать с перегрузкой оператора →
#include <memory>
#include <iostream>
class X
{
public:
void plop()
{
std::cout << "Plop\n";
}
};
class PimplX
{
public:
PimplX()
{
pimpl.reset(new X);
}
X* operator->()
{
return pimpl.get();
}
private:
PimplX(PimplX const&);
PimplX& operator=(PimplX const&);
std::auto_ptr<X> pimpl;
};
int main()
{
PimplX x;
x->plop();
}
Один из вариантов заключается в использовании класса интерфейса:
class ClassA {
virtual void foo() = 0;
static ClassA *create();
};
// in ClassA.cpp
class ImplA : public ClassA {
/* ... */
};
ClassA *ClassA::create() {
return new ImplA();
}
Это, однако, добавляет служебные данные из выборки указателей функции vtable.
Кроме этого, я не могу придумать такого инструмента. Как вы сказали, синтаксический анализ С++ является нетривиальным, и есть другие языки там, где это меньше проблемы (например, С# или Java с задержкой связывают все, поэтому вы можете без проблем менять свой макет памяти).