HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Interface-based polymorphic collection

Submitted by: @import:stackexchange-codereview··
0
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:

#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 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.