Search code examples
c++templatescode-duplication

How to avoid code duplication in this particular case


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.


Solution

  • 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.