views:

76

answers:

5

Is it worth to write classes representing 1D, 2D, 3D points using templates

template <class T>
class Point2D
{

protected:

    T X, Y;

public:

    Point2D(const T x, const T y) : hot smileyx), Y(y)) {}
    ...
};


template <class T>
class Point3D : public Point2D<T>
{
    protected:
    T Z;

public:

    Point3D(const T x, const T y, const T z) : Point2D<T>(x,y), Z(z) {}
...
};

...or using this approach:

class Point2D
{

protected:

    double X, Y;

public:

    Point2D(const double x, const double y) : X(x), Y(y)) {}
    ...
};



class Point3D : public Point2D
{
    protected:
    double Z;

public:

   Point3D(const double x, const double y, const double z) : Point2D(x,y), Z(z) {}
...
};

We understand the coordinates as continuous variables, so it makes sense to express them using double values. A similar situation arises when working with matrices. However in this case templates are widely used...

This class is not only for single use, but it will be part of the library... And my second part of the question. How some "measure" functions should be implemented?

template <class T>
T getDist(const Point2D <T> * p1, const Point2D<T> *p2)
{
....
}

or

double  getDist(const Point2D <T> * p1, const Point2D<T> *p2)
{
....
}

Is it reasonable to write such function in general or for some specific type?

+1  A: 

It looks OK to me, provided that all functions are implemented correctly and efficiently. For distance measurement:

template <class T>
T getDist(const Point3D <T>& p1, const Point3D<T>& p2);

signature should be used. Distances can be in 3D also.

Donotalo
+1  A: 

I've run into this situation, and had cause to allow different types for the X and Y values, where X was sometimes integral but Y always had higher precision (float or double depending on usage). Might be worth considering that upfront.

Steve Townsend
+4  A: 

Why repeat yourself? Most of the content of those classes and functions will be the same.

Something like this works much better:

template <std::size_T N, typename T>
class Point
{
public:
    Point()
    {
        std::fill_n(mData, N, T());   
    }

    explicit Point(const T& pX) :
    mData[0](pX)
    {
        // or some variant (enable_if also works)
        static_assert(N == 1, "X constructor only usable in 1D");
    }


    explicit Point(const T& pX, const T& pY) :
    mData[0](pX),
    mData[1](pY),
    {
        static_assert(N == 2, "XY constructor only usable in 2D");
    }

    // incomplete, left as exercise for reader. :P

private:
    T mData[N];
};

And you just use loops for the functions:

template <std::size_T N, typename T>
T getDist(const Point<N, T>& pFirst, const Point<N, T>& pSecond)
{
    // generic, compiler will unroll loops
}
GMan
Interesting solution... But I would like to keep the point dimension. In some cases it could be useful, some calculations are very different in 2D/3D or may not be realized.
Ian
How about `std::array< T, N > mData;`?
ArunSaha
You could use (partial) template specialization to provide different implementations for Point<2,T> and Point<3,T> where needed.
timday
@Ian: Can you give an example? This is far from complete, too. You'd want to add typedefs and constants and what not. And you can keep going, adding a storage template parameter to switch between storage types if desired, etc.
GMan
Generalization of 2D entities, metrics, algorithms into 3D space. Some examples: Karlsruhe metric, 2D vs 3D Delaunay triangulation...
Ian
GMan
+1  A: 

Based on my experience, I'd avoid like the plague creating a class hierarchy where a 3D point IS-A 2D point. It makes it far too easy for 3D points to unintentionally and silently be treated as 2D points. Better to keep coordinates of different dimensionality distinct types (as per GMan's answer) and require explicit coercion between types.

My current favourite implementation of this sort of thing is provided by Eigen. Even if it's license (LGPL) limits it's use to you, I'd suggest taking a look at it's elegantly templated vector and matrix types and learning from them.

timday
Maybe I do not understand the disadvantages very well... If the class representing points in different dimensions (1D, 2D, 3D ...), when working with instances of it I am not sure what the dimension of the actual point is... More specifically, I can determine the variable type after some test...
Ian
Ah Ok maybe it's me not understanding that you wanted runtime-polymorphism for coordinate dimensionality (which is a fairly unusual requirement I think; everything I've worked on where code was intended to work 2D and 3D was implemented by templating on the coordinate type). But if you really want that, why not just pass variable-size arrays around (e.g std::vectors) for coordinates ? It'd make implementing things like your measure functions easier.
timday
My problem is quite difficult. I am using several types of point. For example common 2D point, 2D point storing topological links or 2D point having cartographic coordinates...
Ian
A: 

Some consideration should also be made on how the class will be used. You mentioned matrices. If you plan on solving systems using that class or anything else incolving matrix inversions (or other similar matrix operations) representing a matrix as a bunch of integers would be outlandish.

If you plan to use it as simply a table of sorts (or are only going to be adding/subtracting and multiplying the matrices) then you could template the class because a matrix full of integers would not provide weird (and incorrect) results for those operations.

Shynthriir