Автоматизировать pimpl'ing классов С++ - есть ли простой способ?

Pimpl является источником шаблона во множестве кода на С++. Они кажутся такими, что может быть решена комбинация макросов, шаблонов и, возможно, небольшая внешняя инструментальная помощь, но я не уверен, каким будет самый простой способ. Я видел шаблоны, которые помогают сделать некоторые из них, но не очень - вам все равно придется писать функции пересылки для каждого метода класс, который вы пытаетесь обернуть. Есть ли более простой способ?

Я представляю инструмент, используемый как часть процесса make. Вы хотите, чтобы ваши публичные заголовки были классами pimpl'd, поэтому вы предоставляете некоторый входной файл, например pimpl.in, который перечисляет классы (реализованные un-pimpl'd), которые вы хотите обернуть, затем этот файл рассматривается, классы pimpl генерируются, и во время "make install" устанавливаются только их заголовки (а не исходные заголовки классов). Проблема в том, что я не вижу никакого способа сделать это без полноценного синтаксического анализатора С++, что даже производители компиляторов не могут получить права. Возможно, классы могут быть написаны каким-то образом, что облегчает работу с внешним инструментом, но я уверен, что в конечном итоге я пропущу всевозможные угловые случаи (например, шаблонные шаблоны и/или шаблонные функции-члены).

Любые идеи? У кого-нибудь еще есть решение для этой проблемы?

Ответ 1

Нет, ответа нет.:-( Я бы подумал, что почти каждый эксперт OO говорит, что "предпочитает композицию над наследованием", там будет поддержка языка для создания композиции намного проще, чем наследование.

Ответ 2

Я не говорю, что это хорошо (просто то, что возникло на ум).
Но вы можете экспериментировать с перегрузкой оператора →

#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();
}

Ответ 3

Один из вариантов заключается в использовании класса интерфейса:

class ClassA {
  virtual void foo() = 0;
  static ClassA *create();
};

// in ClassA.cpp

class ImplA : public ClassA {
/* ... */
};
ClassA *ClassA::create() {
  return new ImplA();
}

Это, однако, добавляет служебные данные из выборки указателей функции vtable.

Кроме этого, я не могу придумать такого инструмента. Как вы сказали, синтаксический анализ С++ является нетривиальным, и есть другие языки там, где это меньше проблемы (например, С# или Java с задержкой связывают все, поэтому вы можете без проблем менять свой макет памяти).