Skip to content

Instantly share code, notes, and snippets.

@ghamarian
Created December 31, 2022 18:37
Show Gist options
  • Save ghamarian/4feb867f9ad40b656f531f1063d9b62c to your computer and use it in GitHub Desktop.
Save ghamarian/4feb867f9ad40b656f531f1063d9b62c to your computer and use it in GitHub Desktop.
#include <algorithm>
#include <concepts>
#include <functional>
#include <iostream>
#include <memory>
#include <vector>
template <typename T>
concept Drawable = requires(T t) {
{ t.draw() } -> std::same_as<void>;
{ t.name() } -> std::same_as<std::string>;
};
// A type-erased wrapper for any object that has a "draw" method
class shape {
public:
// Construct a shape from any object that has a "draw" method
template <Drawable T>
shape(T &&t)
: impl_(std::make_unique<impl<std::decay_t<T>>>(std::forward<T>(t))) {}
// Invoke the "draw" method on the wrapped object
void draw() { impl_->draw(); }
std::string name() { return impl_->name(); }
private:
// A base class for the type-erased implementation
struct impl_base {
virtual void draw() = 0;
virtual std::string name() = 0;
virtual ~impl_base() {}
};
// A concrete implementation for a specific type T
template <Drawable T> struct impl : impl_base {
impl(T t) : obj_(std::move(t)) {}
void draw() override { obj_.draw(); }
std::string name() override { return obj_.name(); }
T obj_;
};
std::unique_ptr<impl_base> impl_;
};
// Example objects that can be type-erased
struct circle {
void draw() { std::cout << "Drawing a circle" << std::endl; }
std::string name() { return "Fancy circle"; }
};
struct square {
void draw() { std::cout << "Drawing a square" << std::endl; }
std::string name() { return "Fancy square"; }
};
int main() {
// Create a shape from a circle and draw it
shape s1(circle{});
s1.draw();
// Create a shape from a square and draw it
shape s2(square{});
s2.draw();
auto q = std::make_unique<shape>(square{});
auto p = std::make_unique<shape>(circle{});
auto z = std::make_unique<shape>(square{});
// one square
std::vector<std::unique_ptr<shape>> shapes;
shapes.emplace_back(std::move(p));
shapes.emplace_back(std::move(q));
shapes.emplace_back(std::move(z));
// call draw on the shapes
// std::for_each(shapes.begin(), shapes.end(), [](auto &s) { std::cout <<
// s->name() << std::endl; }); replace this for each go over the shapes
for (const auto &s : shapes) {
std::cout << s->name() << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment