patterncppMinor
Interface-based polymorphic collection
Viewed 0 times
interfacecollectionpolymorphicbased
Problem
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:
Here is an example of use. Imagine you have an abstract class named
Now, you have some nice classes,
You would like to store
```
template
struct ShapeAdapter:
Shape
{
template
ShapeAdapter(Args&&... args):
data(std::forward(args)...)
{}
virtual void draw() const override
{
data.draw();
}
T data;
};
int ma
#include
#include
#include
#include
template class Adapter>
class poly_collection
{
public:
using iterator = boost::indirect_iterator>::iterator>;
template
auto push(Args&&... args)
-> void
{
auto ptr = new Adapter{ std::forward(args)... };
entities.emplace_back(ptr);
}
template
auto push(T&& other)
-> void
{
auto ptr = new Adapter{ std::forward(other) };
entities.emplace_back(ptr);
}
auto begin()
-> iterator
{
return { std::begin(entities) };
}
auto end()
-> iterator
{
return { std::end(entities) };
}
private:
std::vector> 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
struct ShapeAdapter:
Shape
{
template
ShapeAdapter(Args&&... args):
data(std::forward(args)...)
{}
virtual void draw() const override
{
data.draw();
}
T data;
};
int ma
Solution
As usual with your code, I can only come up with very minor nitpicks.
With both
If
With both
push functions, there is a small chance of a memory leak. After the construction of a new Adapter using:auto ptr = new Adapter{ std::forward(args)... };If
entities.emplace_back(ptr); fails (say if relocation is required, the vector needs to expand, and this fails), then ptr will leak. Instead of doing it this way, I'd suggest using make_unique and moving the unique_ptr into entities using push_back instead:template
auto push(Args&&... args)
-> void
{
auto ptr = std::make_unique>(std::forward(args)...);
entities.push_back(std::move(ptr));
}Code Snippets
auto ptr = new Adapter<T>{ std::forward<Args>(args)... };template<typename T, typename... Args>
auto push(Args&&... args)
-> void
{
auto ptr = std::make_unique<Adapter<T>>(std::forward<Args>(args)...);
entities.push_back(std::move(ptr));
}Context
StackExchange Code Review Q#57173, answer score: 5
Revisions (0)
No revisions yet.