#include <iostream>
#include <memory>
using namespace std;
// наши интерфейсы...
struct IObject;
struct IFoo {
struct Ref; // ссылкообразный тип на данный интерфейс
virtual operator IObject&() = 0; // возвращение к первоисточнику
virtual void f() = 0;
virtual void g() = 0;
};
struct IBar {
struct Ref;
virtual operator IObject&() = 0;
virtual void b() = 0;
virtual void c() = 0;
};
// ссылки - это фасады пимплов
struct IFoo::Ref {
template<class T> struct Thunk; // а это - реализация пимпла
shared_ptr<IFoo> ptr; // лень следить за копиями/ссылками; хочешь, возьми unique_ptr
operator IObject&() { return *ptr; }
void f() { ptr->f(); }
void g() { ptr->g(); }
};
struct IBar::Ref {
template<class T> struct Thunk;
shared_ptr<IBar> ptr;
operator IObject&() { return *ptr; }
void b() { ptr->b(); }
void c() { ptr->c(); }
};
// реализации пимплов
template<class T> struct IFoo::Ref::Thunk : IFoo {
T* ptr; // как и просил - никакого контроля за владением (а может, всё же, хотя бы weak_ptr?)
Thunk(T* p) : ptr(p) {}
operator IObject&() { return *ptr; }
void f() override { ptr->f(); }
void g() override { ptr->g(); }
};
template<class T> struct IBar::Ref::Thunk : IBar {
T* ptr;
Thunk(T* p) : ptr(p) {}
operator IObject&() { return *ptr; }
void b() override { ptr->b(); }
void c() override { ptr->c(); }
};
// порождающая функция проксей
template<class I, class T> // I явный, T выводится
typename I::Ref make_proxy(T* p) {
return typename I::Ref{
make_shared<typename I::Ref::template Thunk<T>>(p)
};
}
// метаинтерфейс (должен быть объявлен после фасадов, ибо возвращает значения)
struct IObject {
virtual operator IFoo::Ref() = 0;
virtual operator IBar::Ref() = 0;
};
// вот наш класс!
struct Something : IObject {
operator IFoo::Ref() override { return make_proxy<IFoo>(this); }
operator IBar::Ref() override { return make_proxy<IBar>(this); }
void f() { cout << "Something::f\n"; }
void g() { cout << "Something::g\n"; }
void b() { cout << "Something::b\n"; }
void c() { cout << "Something::c\n"; }
};
// вот наш объект (один штучка)
Something smth;
IObject* lookup() { return &smth; }
// вот наш клиентский код!
void doit(IFoo::Ref foo) {
foo.f();
foo.g();
IBar::Ref bar = (IObject&)foo; // поскольку IFoo и IBar независимы, делаем up-down-каст
bar.b();
bar.c();
}
int main() {
doit(*lookup());
}