I need to store a container of pointers to objects. These objects have some common methods/attributes (interface) that I want to enforce (possibly at compile time) and use. Example:
struct A{
void fly(){}
};
struct B{
void fly(){}
};
A a;
B b;
std::vector<some *> objects;
objects.push_back(&a);
objects.push_back(&b);
for(auto & el: objects)
el->fly();
The simpler solution would be A
and B
inherit a common base class like FlyingClass
:
struct FlyingClass{
void fly(){}
};
struct A: public FlyingClass { ...
struct B: public FlyingClass { ...
and create a
std::vector<FlyingClass *> objects;
This will work and also enforce the fact that I can only add to objects
things that can fly (implement FlyingClass
).
But what if I need to implement some other common methods/attributes WITHOUT coupling them with the above base class?
Example:
struct A{
void fly(){}
void swim(){}
};
struct B{
void fly(){}
void swim(){}
};
And i would like to do:
for(auto & el: objects) {
el->fly();
...
el->swim();
...
}
More in general i would be able to call a function passing one of these pointers and access both the common methods/attributes, like:
void dostuff(Element * el){
el->fly();
el->swim();
}
I could try to inherit from another interface like:
struct SwimmingClass{
void swim(){}
};
struct A: public FlyingClass, public SwimmingClass { ...
struct B: public FlyingClass, public SwimmingClass { ...
But then what the container should contain?
std::vector<FlyingClass&&SwimmingClass *> objects;
Sure, i could implement SwimmingFlyingClass
, but what if i need RunningClass
etc.. This is going to be a nightmare.
In other words, how can I implement a pointer to multiple interfaces without coupling them?
Or there is some template way of rethinking the problem? Even run time type information could be acceptable in my application, if there is an elegant and maintainable way of doing this.
It is possible to do this, in a pretty TMP-heavy way that's a little expensive at runtime. A redesign is favourable so that this is not required. The long and short is that what you want to do isn't possible cleanly without language support, which C++ does not offer.
As for the ugly, shield your eyes from this:
struct AnyBase { virtual ~AnyBase() {} }; // All derived classes inherit from.
template<typename... T> class Limited {
AnyBase* object;
template<typename U> Limited(U* p) {
static_assert(all<is_base_of<T, U>...>::value, "Must derive from all of the interfaces.");
object = p;
}
template<typename U> U* get() {
static_assert(any<is_same<U, T>...>::value, "U must be one of the interfaces.");
return dynamic_cast<U*>(object);
}
}
Some of this stuff isn't defined as Standard so I'll just run through it. The static_assert
on the constructor enforces that U
inherits from all of T
. I may have U and T the wrong way round, and the definition of all
is left to the reader.
The getter simply requires that U
is one of the template arguments T...
. Then we know in advance that the dynamic_cast
will succeed, because we checked the constraint statically.
It's ugly, but it should work. So consider
std::vector<Limited<Flying, Swimming>> objects;
for(auto&& obj : objects) {
obj.get<Flying>()->fly();
obj.get<Swimming>()->swim();
}
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