Skip to content

Instantly share code, notes, and snippets.

@CyberDNIWE
Created November 15, 2022 12:25
Show Gist options
  • Save CyberDNIWE/7f4476565858e19b4d9ff704ca532e80 to your computer and use it in GitHub Desktop.
Save CyberDNIWE/7f4476565858e19b4d9ff704ca532e80 to your computer and use it in GitHub Desktop.
Type erasure example for dummies (free friend funcs)
#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;
};
#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();
}
// 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;
};
#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();
}
#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;
};
#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"
// 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;
}
#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;
};
#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();
}
#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;
};
#pragma once
#include "ShapeAffordance_draw.h"
#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