Here is a small C++11 utility collection to store any object that satisfies a given interface. Only the basic concepts are provided, I did not try to create a full collection:
#include <memory>
#include <utility>
#include <vector>
#include <boost/iterator/indirect_iterator.hpp>
template <typename Interface, template <typename> class Adapter>
class poly_collection
{
public:
using iterator = boost::indirect_iterator<typename std::vector<std::unique_ptr<Interface>>::iterator>;
template<typename T, typename... Args>
auto push(Args&&... args)
-> void
{
auto ptr = new Adapter<T>{ std::forward<Args>(args)... };
entities.emplace_back(ptr);
}
template<typename T>
auto push(T&& other)
-> void
{
auto ptr = new Adapter<T>{ std::forward<T>(other) };
entities.emplace_back(ptr);
}
auto begin()
-> iterator
{
return { std::begin(entities) };
}
auto end()
-> iterator
{
return { std::end(entities) };
}
private:
std::vector<std::unique_ptr<Interface>> entities;
};
Here is an example of use. Imagine you have an abstract class named Shape
. For the sake of simplicity, it will only have one abstract method, draw
:
struct Shape
{
virtual void draw() const = 0;
virtual ~Shape() {};
};
Now, you have some nice classes, Rectangle
and Circle
, that you didn't write but that satisfy the Shape
interface:
struct Circle
{
Circle(int x, int y, int radius) {}
void draw() const
{
std::cout << "Circle\n";
}
};
struct Rectangle
{
Rectangle(int x, int y, int height, int width) {}
void draw() const
{
std::cout << "Rectangle\n";
}
};
You would like to store Rectangle
and Circle
instances in a colleciton as if they inherited from a common base class. With our solution, you only have to write a new class: ShapeAdapter
, and then feed it to the poly_collection
:
template<typename T>
struct ShapeAdapter:
Shape
{
template<typename... Args>
ShapeAdapter(Args&&... args):
data(std::forward<Args>(args)...)
{}
virtual void draw() const override
{
data.draw();
}
T data;
};
int main()
{
poly_collection<Shape, ShapeAdapter> shapes;
shapes.push<Circle>(1, 2, 3);
shapes.push<Rectangle>(4, 5, 6, 7);
Circle c = { 8, 9, 10 };
shapes.push(c);
for (auto&& shape: shapes)
{
shape.draw();
}
}
So, what do you think of the idea? Would that be an interesting utility? Is there any way I could improve such a collection appart from adding some functions to mimick the standard library's collections ones?
boost::ptr_vector
. \$\endgroup\$