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

Concept based polymorphism

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
conceptpolymorphismbased

Problem

I have a concept based polymorphism example listed below. I allow the user to provide any type that implements the draw method and then I add it into a vector of unique_ptr to concept base. If I have a pointer or reference I want to be able to deal with that even though the solution has value semantics in mind. I thus created a wrapper type which forwards to the correct implementation. I am hoping to get some feedback on if this is the best way to deal with the issue at hand.

#include 
#include 
#include 
#include 

class drawable_concept{
public:
    drawable_concept() = default;
    virtual ~drawable_concept() = default;
    drawable_concept(drawable_concept const&) = delete;
    drawable_concept& operator = (drawable_concept const& ) = delete;
    virtual void draw() = 0;
};

template
class drawable_model : public drawable_concept{
public:
    typedef T model_type;
    drawable_model(T const& model) : model_(model){}
    void draw(){
        model_.draw();
    }
    ~drawable_model() = default;
private:
    T model_;
};

template
class drawable_forwarder{
public:

    drawable_forwarder(T const& item) : item_(item){}

    inline void draw(){
        item_->draw();
    }
private:
    T item_;
};

class graphics_surface{
public:
    void render(){
        std::for_each(std::begin(controls_), std::end(controls_), [] (std::unique_ptr const& control){
            control->draw();
        });
    }

    template
    void push_back(T control){
        auto t = new drawable_model(std::move(control));
        controls_.push_back(std::unique_ptr(t));
    }
private:
    std::vector> controls_;
};

struct triangle{
    void draw(){
        std::cout  ptr(new triangle);
    drawable_forwarder> fwd(ptr);
    surface.push_back(fwd);

    surface.render();

    return 0;
}

Solution

There is not much to be said actually, the concept-based polymorphism seems to be well implemented. You can slightly improve your push_back method by having it emplace_back the std::unique_ptr. It will be a little bit less verbose:

template
void push_back(T control){
    auto t = new drawable_model(std::move(control));
    controls_.emplace_back(t);
}


Also, I suppose that a draw shouldn't alter the object being drawn. Therefore, you better const-qualify all of your draw methods.

You could also be more consistent with the way you use inline: you inlined drawable_forwarder's draw method but not drawable_model while it basically does the same thing.

One fun addition would be to write a method that would create the drawable_model object in-place. That would require to add the following constructor to drawable_model:

template
drawable_model(Args&&... args):
    model_(std::forward(args)...)
{}


And the following method to graphics_surface (not sure about the name):

template
void emplace_back(Args&&... args)
{
    auto t = new drawable_model(std::forward(args)...);
    controls_.emplace_back(t);
}


Then, you could use it this way:

int main()
{
    graphics_surface surface;

    // Assume triangle and rectangle have complete constructors
    surface.emplace_back(3.5, 6.8, 4.8, 3.2);
    surface.emplace_back(/* whatever */);

    surface.render();
}

Code Snippets

template<class T>
void push_back(T control){
    auto t = new drawable_model<T>(std::move(control));
    controls_.emplace_back(t);
}
template<typename... Args>
drawable_model(Args&&... args):
    model_(std::forward<Args>(args)...)
{}
template<typename T, typename... Args>
void emplace_back(Args&&... args)
{
    auto t = new drawable_model<T>(std::forward<Args>(args)...);
    controls_.emplace_back(t);
}
int main()
{
    graphics_surface surface;

    // Assume triangle and rectangle have complete constructors
    surface.emplace_back<triangle>(3.5, 6.8, 4.8, 3.2);
    surface.emplace_back<square>(/* whatever */);

    surface.render();
}

Context

StackExchange Code Review Q#41879, answer score: 4

Revisions (0)

No revisions yet.