Created
November 15, 2022 12:25
-
-
Save CyberDNIWE/7f4476565858e19b4d9ff704ca532e80 to your computer and use it in GitHub Desktop.
Type erasure example for dummies (free friend funcs)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <iostream> | |
class Circle | |
{ | |
public: | |
Circle() : m_radius(0) | |
{}; | |
Circle(int radius) : m_radius(radius) | |
{}; | |
void drawSelf() const noexcept | |
{ | |
std::cout << "Drawed a [CIRCLE] with R: " << m_radius << '\n'; | |
} | |
private: | |
int m_radius; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Drawer_Circle.h" | |
#include "Circle.h" | |
using namespace drawers; | |
void drawers::draw(const Circle& obj) noexcept | |
{ | |
// Each free draw function can do whatever (here for simplicity just call memberfunc) | |
obj.drawSelf(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Shape requires used underlying types to have a free drawers::draw function | |
// this is the one for Circle | |
class Circle; | |
namespace drawers | |
{ | |
void draw(const Circle& obj) noexcept; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Drawer_Square.h" | |
#include "Square.h" | |
using namespace drawers; | |
void drawers::draw(const Square& obj) noexcept | |
{ | |
// Each free draw function can do whatever (here for simplicity just call memberfunc) | |
obj.drawOneself(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
// Shape requires used underlying types to have a free drawers::draw function | |
// this is the one for Square | |
//#include "Square.h" | |
class Square; | |
namespace drawers | |
{ | |
void draw(const Square& obj) noexcept; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
// Shape requires used underlying types to have a free drawers::draw function, gather them here | |
#include "Drawer_Circle.h" | |
#include "Drawer_Square.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Type erasure excercise on the basis of the following video by Klaus Iglberger | |
// https://www.youtube.com/watch?v=4eeESJQk-mw&t=3060s | |
#include <iostream> | |
#include <vector> | |
// Imaginary scenario: we have a Circle and a Square. No relation. | |
// However we want to treat them in a similar fashion to for example draw them. | |
// Each of them has different draw()ing function signature, so no direct inheritance can be done. | |
#include "Circle.h" | |
#include "Square.h" | |
// Shape erases types given to it and enforces calling convention to drawers::- functions | |
#include "Shape.h" | |
// Shape requires used underlying types to have a free drawers::draw function | |
// Need to provide those for each concrete drawable object | |
#include "Drawers.h" | |
// Just a function to draw any shape by calling free affordance function | |
void drawAllShapes(const std::vector<Shape>& shapes) noexcept | |
{ | |
for(const auto& shape : shapes) | |
{ | |
shape_affordances::draw(shape); | |
} | |
} | |
int main() | |
{ | |
using Shapes = std::vector<Shape>; | |
Shapes shapes = {}; | |
shapes.reserve(8); | |
// Make shapes | |
shapes.emplace_back(Circle( )); | |
shapes.emplace_back(Circle(1)); | |
shapes.emplace_back(Square( )); | |
shapes.emplace_back(Square(2)); | |
shapes.emplace_back(Circle(4)); | |
// Draw all shapes | |
drawAllShapes(shapes); | |
//end | |
std::getchar(); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <memory> | |
#include "ShapeAffordances.h" | |
// Erases the type for all instantiated shapes, | |
// forcing unified usage of draw() affordance via free function | |
// overloaded for each concrete undelying type in drawers namespace | |
class Shape | |
{ | |
private: | |
struct ShapeConcept | |
{ | |
virtual ~ShapeConcept() = default; | |
virtual void draw() const = 0; | |
virtual std::unique_ptr<ShapeConcept> clone() const = 0; | |
}; | |
template<typename T> | |
struct ShapeModel : ShapeConcept | |
{ | |
ShapeModel(const T&& val) : storedObject(std::forward<const T>(val)) | |
{}; | |
// Our ShapeModel affords draw()ing (required by ShapeConcept) by delegating it to appropriate drawers::draw(...) function | |
virtual void draw() const override | |
{ | |
drawers::draw(storedObject); | |
} | |
virtual std::unique_ptr<ShapeConcept> clone() const override | |
{ | |
return std::make_unique<ShapeModel>(*this); | |
}; | |
T storedObject; | |
}; | |
// Friend affordance functions declarations, needed for friend function to access pimpl | |
friend void shape_affordances::draw(const Shape& shape) noexcept; | |
std::unique_ptr<ShapeConcept> pimpl; | |
public: | |
template<typename T> | |
Shape(const T&& x) : | |
pimpl(new ShapeModel<T>(std::forward<const T>(x))) //<--- type erased at this point | |
{}; | |
// Special member functions | |
Shape(const Shape& s) | |
{ | |
pimpl = s.pimpl->clone(); | |
} | |
Shape(Shape&& s) | |
{ | |
pimpl = std::move(s.pimpl); | |
} | |
Shape& operator=(const Shape& s) = default; | |
Shape& operator=(Shape&& s) = default; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ShapeAffordance_draw.h" | |
#include "Shape.h" | |
using namespace shape_affordances; | |
void shape_affordances::draw(const Shape& shape) noexcept | |
{ | |
//if(shape.pimpl) | |
shape.pimpl->draw(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
class Shape; | |
namespace shape_affordances | |
{ | |
// Affords draw()ing of Shape, delegates it to shape's pimpl (since only it knows its underlying type) | |
void draw(const Shape& shape) noexcept; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "ShapeAffordance_draw.h" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <iostream> | |
class Square | |
{ | |
public: | |
Square(int x = 0, int y = 0) : m_side(x) | |
{}; | |
void drawOneself() const noexcept | |
{ | |
std::cout << "Drawed a [SQUARE] with L:" << m_side << '\n'; | |
} | |
private: | |
int m_side; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment