I have these generic classes for 2D geometry:
template<class T> struct Point
{
T x,y;
//...Various member functions...
//T modulus() const noexcept { ... }
//T dist_from(const Point& other) const noexcept { ... }
//...
};
template<class T> class Polygon
{
public:
// ...An awful lot of member functions...
auto size() const noexcept { return vertexes.size(); }
//T area() const noexcept {...}
//T perimeter() const noexcept {...}
//T moment_of_inertia(const Point<T>&) const noexcept {...}
//void reorder_vertexes() {...}
//void transform(const Matrix& m) {...}
// ...
private:
std::vector<Point<T>> vertexes;
};
They work well and I use them in various part of the project. Now, for a particular application, I need Polygon
but also need to associate some data
to each of its vertexes.
Since Polygon
vertexes can be transformed and reordered I would prefer to avoid the extra work of maintaining a parallel std::vector
containing the additional data
.
I would really like to introduce a new class like this:
template<class T, class D> class DecoratedPolygon
{
public:
struct DecoratedPoint
{
Point<T> point;
D data;
};
// Some specialized member functions...
const D& get_vertex_decoration(const std::size_t idx) const
{
return vertexes.at(idx).data;
}
// ...Then same member functions as polygon,
// except accessing the '.point'
auto size() const noexcept { return vertexes.size(); }
//...
private:
std::vector<DecoratedPoint> vertexes;
};
My problem whith this is that I don't want to rewrite a slightly modified version of all the member functions of Polygon
.
How would you approach this particular case? I wonder if there is a zero cost technique to avoid that code duplication, or if simply I'm heading in the wrong way.
You could try to parameterize the Polygon class to take in the Point type as well:
template <class P> class Polygon {
std::vector<P> vertices
}
template <class T> class Point {
T x,y;
}
class DecoratedPoint : Point<long> {
int extraData;
}
Polygon<DecoratedPoint> newPoly;
The decorated data would end up coming from the point itself though, so the interface would look a bit different:
Polygon {
P get_point_at_idx( const std::size_t idx) {
return points.at(idx)
}
}
my_poly.get_point_at_idx(0).extraData
You'd need to change the implementation of Polygon a bit, but might provide more control longer term.