C ++ return vector of ptr interface
I am trying to create an interface that returns an interface vector using it on the other side.
I know it is not clear, so here are the examples:
class IComponent
{
public:
virtual ~IComponent() {}
virtual int getValue() const = 0;
};
class ICollection
{
public:
virtual ~ICollection() {}
virtual IComponent* getComp() = 0;
};
class Component : public IComponent
{
public:
virtual int getValue() const { return 42; }
};
class Collection : public ICollection
{
public:
Collection()
{
m_comp = new Component();
}
virtual Component* getComp()
{
return m_comp;
}
private:
Component* m_comp;
};
This compilation works because it is Component*
implicitly converted to IComponent*
. This allows me to use fully implemented Collection
and Component
.
#include <vector>
class IComponent
{
public:
virtual ~IComponent() {}
virtual int getValue() const = 0;
};
class ICollection
{
public:
virtual ~ICollection() {}
virtual std::vector<IComponent*> &getComp() = 0;
};
class Component : public IComponent
{
public:
virtual int getValue() const { return 42; }
};
class Collection : public ICollection
{
public:
Collection()
{
m_comp.push_back(new Component());
m_comp.push_back(new Component());
}
virtual std::vector<Component*> &getComp()
{
return m_comp;
}
private:
std::vector<Component*> m_comp;
};
But this time, I cannot work with the fully implemented collection and component.
Of course, it's easy to see why, because there Component *
is IComponent *
, but strictly speaking std::vector<Component *>
not std::vector<IComponent *>
. This issue also applies to all types of templates, such as iterators, smart_ptr, etc.
Instead, Collection::getComp()
I need to return std::vector<IComponent *>
. I can possibly turn it on m_comp
by returning it (I'm not even sure if it works, wouldn't it be an rvalue?). I can also store 2 vector
, one of Component *
and one of IComponent *
, and keep them in sync, but that's ugly code I guess.
Is there some workaround, or was I somehow unaware of this?
ADDITIONAL INFORMATION:
I would really like to be able to use Component
in Collection
. If I change m_comp
to std::vector<IComponent *>
and the return type getComp()
works, but I am forced to use pointers to IComponent
and not Component
to Collection
.
So yes, I can sync two vectors, or create a copy in getComp()
, but I want to know if there is a better way to do this.
EVENT MORE DETAILS:
Here is an example of how I would like to use it: (I just added the method getDouble()
in Component
and called it in Collection
)
#include <iostream>
#include <vector>
class IComponent
{
public:
virtual ~IComponent() {}
virtual int getValue() const = 0;
};
class ICollection
{
public:
virtual ~ICollection() {}
virtual std::vector<IComponent*> &getComp() = 0;
};
class Component : public IComponent
{
public:
virtual int getValue() const { return 42; }
double getDouble() const { return 3.14; }
};
class Collection : public ICollection
{
public:
Collection()
{
m_comp.push_back(new Component());
m_comp.push_back(new Component());
}
// Impossible because it does not override the good method
virtual std::vector<Component*> &getComp()
{
std::cout << m_comp[0]->getDouble() << std::endl;
return m_comp;
}
private:
std::vector<Component*> m_comp;
};
If I change the return type, the Collection becomes:
class Collection : public ICollection
{
public:
Collection()
{
m_comp.push_back(new Component());
m_comp.push_back(new Component());
}
virtual std::vector<IComponent*> &getComp()
{
// This time this line is impossible without cast
// because m_comp[0] is IComponent*
std::cout << m_comp[0]->getDouble() << std::endl;
return m_comp;
}
private:
std::vector<IComponent*> m_comp;
};
One solution would be like this:
class Collection : public ICollection
{
public:
Collection()
{
// Useless and redundant code here
m_comp.push_back(new Component());
m_icomp.push_back(m_comp.back());
m_comp.push_back(new Component());
m_icomp.push_back(m_comp.back());
}
// Impossible because it does not override the good method
virtual std::vector<IComponent*> &getComp()
{
std::cout << m_comp[0]->getDouble() << std::endl;
return m_icomp;
}
private:
// Duplicated storage for "nothing"
std::vector<Component*> m_comp;
std::vector<IComponent*> m_icomp;
};
In the answers I saw there was something in the implementation of the method pop()
and push()
, but in my project I use Map
instead of Collection
, and there is a vector of blocks. This allows you to access the block by executing map[y][x]
.
The problem is the repository is fixed. One solution would be to add a method instead getBlock(size_t x, size_t y)
. But I want to see what I could do in the first case :)
source to share
Not sure if this solution might work in your case, but ... what about passing a derived class Component
as a template parameter in ICollection
?
Not exactly curious about the repeating pattern, but something similar.
I mean, something like
template <typename CompT>
class ICollection
{
public:
virtual ~ICollection() {}
virtual std::vector<CompT*> &getComp() = 0;
};
So Collection
become
class Collection : public ICollection<Component>
{
public:
Collection()
{
m_comp.push_back(new Component());
m_comp.push_back(new Component());
}
virtual std::vector<Component*> &getComp()
{
return m_comp;
}
private:
std::vector<Component*> m_comp;
};
or even if you convert Collection
to template class
template <typename CompT>
class Collection : public ICollection<CompT>
{
public:
Collection()
{
m_comp.push_back(new CompT());
m_comp.push_back(new CompT());
}
virtual std::vector<CompT*> &getComp()
{
return m_comp;
}
private:
std::vector<CompT*> m_comp;
};
source to share
If you know that the elements of the return are vector
pointing to objects Component
, you can:
Collection coll;
std::vector<IComponent*>& v = coll.getComp();
Component& comp = *dynamic_cast<Component*>(v[0]);
If you only want virtual methods from the interface IComponent
, you don't even need to throw; you can just use dynamic dispatch to call them. But casting to a reference of the child type will give you whatever methods it adds.
I also suggest making your abstraction a little less leaky. You can access items through operator[]
, push()/pop()
or something makes sense. Then the internal storage can be any container of any pointer type, and the public interface should not change.
A less secure alternative is to go back to path C. You can take m_comp.data()
as IComponent**
, give it to, Component**
and return it.
source to share
In any case, you need to change the return type Collection::getComp
to std::vector<IComponent*>&
, because otherwise it cannot override ICollection::getComp
. Since a std::vector<IComponent*>
can also contain pointers to Component
, just change your member to it and you should be fine.
If pointers to Component
, rather than its interface, are required somewhere, they can be disabled using dynamic_cast .
source to share