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 <iostream>
#include <vector>
#include <memory>
#include <algorithm>
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 T>
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 T>
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<drawable_concept> const& control){
control->draw();
});
}
template<class T>
void push_back(T control){
auto t = new drawable_model<T>(std::move(control));
controls_.push_back(std::unique_ptr<drawable_concept>(t));
}
private:
std::vector<std::unique_ptr<drawable_concept>> controls_;
};
struct triangle{
void draw(){
std::cout << "Triangle" << std::endl;
}
};
struct square{
void draw(){
std::cout << "Square" << std::endl;
}
};
int main(int argc, const char * argv[])
{
graphics_surface surface;
surface.push_back(triangle());
surface.push_back(square());
std::shared_ptr<triangle> ptr(new triangle);
drawable_forwarder<std::shared_ptr<triangle>> fwd(ptr);
surface.push_back(fwd);
surface.render();
return 0;
}
1 Answer 1
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<class T>
void push_back(T control){
auto t = new drawable_model<T>(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<typename... Args>
drawable_model(Args&&... args):
model_(std::forward<Args>(args)...)
{}
And the following method to graphics_surface
(not sure about the name):
template<typename T, typename... Args>
void emplace_back(Args&&... args)
{
auto t = new drawable_model<T>(std::forward<Args>(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<triangle>(3.5, 6.8, 4.8, 3.2);
surface.emplace_back<square>(/* whatever */);
surface.render();
}
drawable_concept
is an interface. Concepts relate to a template mechanism that might come in C++14. \$\endgroup\$