I have this pimpl design where the implementation classes are polymorphic but the interfaces are supposed to just contain a pointer, making them polymorphic somewhat defeats the purpose of the design.
So I create my Impl and Intf base classes to provide reference counting. And then the user can create their implementations. An example:
class Impl {
mutable int _ref;
public:
Impl() : _ref(0) {}
virtual ~Impl() {}
int addRef() const { return ++_ref; }
int decRef() const { return --_ref; }
};
template <typename TImpl>
class Intf {
TImpl* impl;
public:
Intf(TImpl* t = 0) : impl(0) {}
Intf(const Intf& other) : impl(other.impl) { if (impl) impl->addRef(); }
Intf& operator=(const Intf& other) {
if (other.impl) other.impl->addRef();
if (impl && impl->decRef() <= 0) delete impl;
impl = other.impl;
}
~Intf() { if (impl && impl->decRef() <= 0) delete impl; }
protected:
TImpl* GetImpl() const { return impl; }
void SetImpl(... //etc
};
class ShapeImpl : public Impl {
public:
virtual void draw() = 0;
};
class Shape : public Intf<ShapeImpl> {
public:
Shape(ShapeImpl* i) : Intf<ShapeImpl>(i) {}
void draw() {
ShapeImpl* i = GetImpl();
if (i) i->draw();
}
};
class TriangleImpl : public ShapeImpl {
public:
void draw();
};
class PolygonImpl : public ShapeImpl {
public:
void draw();
void addSegment(Point a, Point b);
};
Here is where have the issue. There are two possible declaration for class Polygon:
class Polygon1 : public Intf<PolygonImpl> {
public:
void draw() {
PolygonImpl* i = GetImpl();
if (i) i->draw();
}
void addSegment(Point a, Point b) {
PolygonImpl* i = GetImpl();
if (i) i->addSegment(a,b);
}
};
class Polygon2 : public Shape {
void addSegment(Point a, Point b) {
ShapeImpl* i = GetImpl();
if (i) dynamic_cast<Polygon*>(i)->addSegment(a,b);
}
}
In the Polygon1, I have rewrite the code for draw because I have not inherited it. In Polygon2 I need ugly dynamic casts because GetImpl() doesn't know about PolygonImpl. What I would like to do is something like this:
template <typename TImpl>
struct Shape_Interface {
void draw() {
TImpl* i = GetImpl();
if (i) i->draw();
}
};
template <typename TImpl>
struct Polygon_Interface : public Shape_Interface<Timpl> {
void addSegment(Point a, Point b) { ... }
};
class Shape : public TIntf<ShapeImpl>, public Shape_Interface<ShapeImpl> {...};
class Polygon : public TIntf<PolygonImpl>, public Polygon_Interface<PolygonImpl> {
public:
Polygon(PolygonImpl* i) : TIntf<PolygonImpl>(i) {}
};
But of course there's a problem here. I can't access GetImpl() from the Interface classes unless I derive them from Intf. And if I do that, I need to make Intf virtual everywhere it appears.
template <typename TImpl>
class PolygonInterface : public virtual Intf<TImpl> { ... };
class Polygon : public virtual Intf<PolygonImpl>, public PolygonInterface { ... }
OR I can store a TImpl*& in each Interface and construct them with a reference to the base Intf::impl. But that just means I have a pointer pointing back into myself for every interface included.
template <typename TImpl>
class PolygonInterface {
TImpl*& impl;
public:
PolygonInterface(TImpl*& i) : impl(i) {}
...};
Both of these solutions bloat the Intf class, add an extra dereference, and basically provide no benefit over straight polymorphism.
So, the question is, is there a third way, that I've missed that would solve this issue besides just duplicating the code everywhere (with its maintenance issues)?
TOTALLY SHOULD, BUT DOESN'T WORK: I wish there were base classes unions that just overlaid the class layouts and, for polymorphic classes, required that they have the exact same vtable layout. Then both Intf and ShapeInterface would each declare a single T* element and access it identically:
class Shape : public union Intf<ShapeImpl>, public union ShapeInterface<ShapeImpl> {};