In a project I'm working on we're using a pattern where we have pure abstract classes defining an interface, and template implementations that may be instantiated with different template parameters, which are used for many things, including dependency injection. Something like:
struct Base {
virtual int getVal() = 0;
};
template <typename DI>
struct BaseImpl : public Base {
int getVal() override { return DI::k_Val; }
};
I guess question zero is whether this is a known/common/formal pattern, and if so, what's its name?
A need that commonly arises is to extend the Base
interface, say to Inherited
. Any Inherited
object should still polymorphically function as a Base
, but also as Inherited
, which may define additional methods. Something like:
struct Inherited : public Base {
virtual int getOtherVal() = 0;
};
The problem lies in re-using the implementation of BaseImpl<>
in InheritedImpl<>
. Because InheritedImpl<>
would need to inherit from Inherited
and BaseImpl<>
we run into the "dreaded dimond" problem, so we have to use virtual inheritance. See working example in godbolt.
struct Base {
virtual int getVal() = 0;
};
template <typename DI>
struct BaseImpl : public virtual Base {
int getVal() override { return DI::k_Val; }
};
struct Inherited : public virtual Base {
virtual int getOtherVal() = 0;
};
template <typename DI>
struct InheritedImpl : public Inherited, public BaseImpl<DI> {
int getOtherVal() override { return DI::k_OtherVal; }
};
void useBase(Base& base) {
std::cout << "getVal: " << base.getVal() << std::endl;
}
void useInherited(Inherited& inherited) {
std::cout << "getOtherVal: " << inherited.getOtherVal() << std::endl;
}
struct DI {
static constexpr int k_Val = 1;
static constexpr int k_OtherVal = 2;
};
int main() {
auto base = std::make_unique<BaseImpl<DI>>();
auto inherited = std::make_unique<InheritedImpl<DI>>();
useBase(*base);
useBase(*inherited);
useInherited(*inherited);
}
getVal: 1 getVal: 1 getOtherVal: 2
Because of the issues with virtual inheritance I'd like to avoid using it here, if possible. Are there any alternatives in this case? Is this potentially a code smell? Any architectural advice?