Те, кто хоть сколько-нибудь программировал на C++ знают, как бесит ситуация, когда приватные детали класса тянут за собой кучу ненужного для пользователя класса барахла:
#include <junk1.h>
#include <junk2.h>
class Foo {
public:
static std::unique_ptr<Foo> Make();
void DoWork();
private:
Foo();
Junk1 left_hand;
Junk2 right_hand;
};
Пользователь работает с классом только через Make
и DoWork
, но из-за того, что нельзя сделать forward declaration отдельно для методов – приходится ребилдить свой код при каждом изменении junk1 и junk2.
Классические способ решения проблемы:
- Популярный Pimpl – спрятать Junk1 и Junk2 в другой класс, а в хедере оставить указатель на forward declaration. Минусы: ручной
this
, двойное разыменование;
- Сделать DoWork виртуальным, а в фабрике возвращать наследника. Минусы: неявный вызов, много boilerplate.
- Аналогично виртуальным методам, но кастить
Foo*
на FooImpl*
. Опять boilerplate, но быстро.
Но упарываясь очередной ночью без сна мне пришла иная мысль. Ведь по сути мы хотим сделать forward declaration функции с неявным this, но синтаксис нам этого не позволяет. Но что если взять расширение компилятора, и разрешить проблему на уровне линковщика? Вуаля:
#include <stdio.h>
struct Foo {
int add(int a, int b) asm("_ZN7FooImpl3addEii");
protected:
Foo() noexcept = default;
};
struct FooImpl : Foo {
using Foo::Foo;
int add(int a, int b);
int x;
};
int FooImpl::add(int a, int b) {
printf("%s: Adding %d with %d:\t Result:%d\n",
__PRETTY_FUNCTION__, a, b, a+b+x);
return a+b+x;
}
int main()
{
FooImpl obj;
obj.x = 10;
obj.add(3, 6);
Foo &erased = obj;
erased.add(3, 5);
return 0;
}
Код работает на всех версиях gcc, clang, icc. Для GCC также работает __attribute__ ((alias ("_ZN7FooImpl3addEii")))
.
Как думаете, был бы полезен плагин gcc/clang, реализующий атрибут на уровне класса, чтобы для undefined symbols автоматически делать переименования символа?