A library I use has many types, all of which derive from the same 2 interfaces:
class Huey : public IDuck, public ICartoonCharacter
{
...
};
class Dewey : public IDuck, public ICartoonCharacter
{
...
};
class Louie : public IDuck, public ICartoonCharacter
{
...
};
I'd like to store objects of all the above types in a wrapper class and stick objects of that wrapper class in a container. Of course I should be able to call methods belonging to both interfaces from my wrapper class.
What are my options here? I could think of
IDuck *
s in my wrapper and dynamic_cast-ing to ICartoonCharacter
, orboost::any
while making my wrapper a class-template, with a couple of static_asserts
to ensure the template parameter inherits from IDuck
and ICartoonCharacter
.but neither option particularly appeals. Any ideas?
two interfaces, multiple inheritance combine into one container? is a related question, but James Kanze's answer doesn't work for me, as I can't change the 3 classes.
EDIT: Don't use multiple inheritance often, had forgotten syntax. Now inheriting public
ly from both interfaces.
EDIT: Now using dynamic_cast instead of static_cast (which won't work).
EDIT: I found both Mike Seymour's and Matthieu M's answers promising. I'll accept one of their answers once I've coded it all up. Thanks!
A simple option is to store two pointers in the wrapper:
struct CartoonDuckWrapper {
IDuck * duck;
ICartoonCharacter * toon;
template <class CartoonDuck>
CartoonDuckWrapper(CartoonDuck & cd) : duck(&cd), toon(&cd) {}
};
There's no particular need to use static_assert
to check that CartoonDuck
inherits from both base classes, although that might give slightly better diagnostics than simply letting the pointer conversions fail.
If the base classes are polymorphic (which, being interfaces, they probably are), you could save the space of one pointer, in exchange for a run-time cost, by using dynamic_cast
to convert one to the other. static_cast
can't be used for such a "cross-cast" between base classes.
As all issues in programming, you can solve it by adding one more level of indirection.
class ICartoonDuck: public IDuck, public ICartoonCharacter {};
template <typename T>
class CartoonDuck: public ICartoonDuck {
public:
explicit CartoonDuck(T t): _t(std::move(t)) {}
// IDuck interface
virtual void foo() override { t.foo(); }
// ICartoonCharacter interface
virtual void bar() override { t.bar(); }
private:
T _t; // or any ownership scheme that makes sense
}; // class CartoonDuck
template <typename T>
CartoonDuck<T> makeCartoonDuck(T t) { return CartoonDuck(std::move(t)); }
template <typename T, typename... Args>
std::unique_ptr<CartoonDuck<T>> makeUniqueCartoonDuck(Args&&...) {
return std::unique_ptr<CartoonDuck<T>>(new T(std::forward<Args>()...);
}
Now, you can happily store std::unique_ptr<ICartoonDuck>
in your container.
This can be used as:
std::vector<std::unique_ptr<ICartoonDuck>> cartoonDucks;
cartoonDucks.push_back(makeUniqueCartoonDuck<Huey>());
cartoonDucks.push_back(makeUniqueCartoonDuck<Dewey>());
cartoonDucks.push_back(makeUniqueCartoonDuck<Louie>());
for (std::unique_ptr<ICartoonDuck> const& cd: cartoonDucks) {
cd->foo();
cd->bar();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With