3

I am using a data model with classes: Point2D, Point3D, PointGeo:

template <class T>
class Point2D
{
    protected:
            T x;
            T y;
...
};


template <class T>
class Point3D
{
    protected:
            T x;
            T y;
            T z;
...
};

template <class T>
class PointGeo
{
    protected:
            T lat;
            T lon;
...
};

To manage instances of these classes the folowing classes allowing loading points from file, adding/removing points, clearing list, printing are used...

List of 2D points

template <class T>
struct TPoints2DList
{
    typedef std::vector <Point2D <T> > Type;
};


template <class T>
class Points2DList
{
    private:
            typename TPoints2DList <T>::Type  points;


    public:
            Points2DList() : points ( 0 ) {}
            virtual ~Points2DList() {points.clear();}
            Points2DList ( const Points2DList &source );
            typename TPoints2DList <T>::Type ::iterator begin() { return points.begin(); }
            typename TPoints2DList <T>::Type::const_iterator begin() const { return points.begin(); }
            typename TPoints2DList <T>::Type::iterator end() { return points.end(); }
            typename TPoints2DList <T>::Type::const_iterator end() const { return points.end(); }
            Point2D <T> &operator [] ( int index ) {return points[index];}
            const Point2D <T> &operator [] ( int index ) const {return points[index];}

    public:
            //Overloaded member functions
            inline void clear() {points.clear();};
            inline void pop_back() {points.pop_back();}
            inline void push_back ( Point2D <T> p ) { points.push_back ( p );}
            inline unsigned int size() const {return points.size();}

    public:
            //Other methods
            void loadPointsFromFile ( const char *file);
...
}

List of 3D points

template <class T>
struct TPoints3DList
{
    typedef std::vector <Point3D <T> > Type;
};


template <class T>
class Points3DList
{
    private:
            typename TPoints3DList <T>::Type  points;


    public:
            Points3DList() : points ( 0 ) {}
            virtual ~Points2DList() {points.clear();}
            Points3DList ( const Points3DList &source );
            typename TPoints3DList <T>::Type ::iterator begin() { return points.begin(); }
            typename TPoints3DList <T>::Type::const_iterator begin() const { return points.begin(); }
            typename TPoints3DList <T>::Type::iterator end() { return points.end(); }
            typename TPoints3DList <T>::Type::const_iterator end() const { return points.end(); }
            Point3D <T> &operator [] ( int index ) {return points[index];}
            const Point3D <T> &operator [] ( int index ) const {return points[index];}

    public:
            inline void clear() {points.clear();};
            inline void pop_back() {points.pop_back();}
            inline void push_back ( Point3D <T> p ) { points.push_back ( p );}
            inline unsigned int size() const {return points.size();}

    public:
            //Other methods
            void loadPointsFromFile ( const char *file);
 ...
}

Source code of PointGeo class is similar...

So differences in the code between the classes are small. They differ in methods for loading, printing, saving data.

Would it be inappropriate to design a class replacing all three classes? How to create methods for loading, printing data specific for the data type?

The similar situation occurs for the dynamic alocation: Node2D, Node3D,... classes. Class Node2D stores some topological relationships and using pointers to other nodes or faces... In such case all three classes will have a different destructor...

List of 2D points

template <class T>
struct TNodes2DList
{
    typedef std::vector <Node2D <T> *> Type;
};

Thank you very much for your comments and suggestions. I am writing geometric library and thinking about the most suitable data model.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ian
  • 51
  • 2
  • 4
  • 1
    Many questions today with Point2D, TPoints2DList and TPoints3DList ?!?! Looks like homework assignment!!! http://stackoverflow.com/questions/4502268/c-template-typedef – yasouser Dec 21 '10 at 18:59
  • My question is not a homework, it relates to the data model for my library (please, look down). The library has been finished and I am thinking about improvements for my data model... Thanks for your link... – Ian Dec 21 '10 at 19:22

2 Answers2

4

You could put the I/O operations directly to Point2D Point3D types. Then the you wouldn't have to create additional list class since printing/reading would be as simple as:

for (std::vector<Point3D>::iterator i = a.begin; i != a.end(); ++i) {
    i->print_to_file(file);
}

If this isn't viable, at least the same list class could be used as template to serve both Point3D and Point2D

  • If you're going that way, you may as well use std::for_each as well, and stop naming extra types and variables. – Phil Miller Dec 21 '10 at 19:50
  • But there are not only printing operations, that have to be data type specific... This issue will also apply to destructors (there are pointers to another nodes in dynamically allocated nodes)... This situation could be simplified by using shared_ptr. But it means completely rewrite the application, and to "sacrifice" some performance... – Ian Dec 22 '10 at 10:10
  • @Novelocrat: I find std::for_each quite inconvenient, especially in these small loops. Still, when range based for-loops become supported, there'll be no benefits to use std::for_each except for several rare cases –  Dec 26 '10 at 03:50
1

Depending on what you are doing with the classes, you could do something like:

template<class T, unsigned int count = 2>
class Point
{
public:
    T t[count];
    // other data members here
};

However, if you are doing something like vector math (e.g. cross products, dot products, normalization, etc.) doing this would make the class far more complicated than having 2 classes (2D and 3D). Unless you are doing something specialized in the Geo class, there really isn't any need to have a different 2D and Geo form of it.

Zac Howland
  • 15,777
  • 1
  • 26
  • 42
  • This library is focused on the geometry, computational geometry and mathematical cartography. I have already completed all core algorithms and thinking about simplification of the code... – Ian Dec 21 '10 at 19:05
  • Are there functions in each class implementation that handle things like normalization, vector multiplication, angle calculations, etc. differently between the 2D class and the Geo class? (If you are doing any of those in the 3D class, you'd probably want to implement it as a separate class since the specialization would be that anyway). If not, then combining those classes into a single class would simplify it fairly well. – Zac Howland Dec 21 '10 at 19:24
  • Functions you mentioned are stored in geometric kernel which consist of several classes. Each class has static methods. Input parameter is for example 2D geographic point and output parameter 3D Cartesian point (or lists of Points)... – Ian Dec 21 '10 at 19:32
  • In that case, if all these template classes are doing is storing data, there is no need to have them in separate templates. You could use the format described and have a single template class definition. For Geometric data, you could end up with a function that takes an input of type `Point` and has an output of `Point`. – Zac Howland Dec 21 '10 at 19:35
  • But situation is not easy, as it looks. From Point2D I derived new class Point2DProj storing cartographic properties of the point (i.e. distortions). One specialization representing the data dimension is not enough... And there will be a problem in "dynamic" versions of classes. They stores different topological relationships for each dimensions as pointers, so destructors can not be written for all cases universally. – Ian Dec 21 '10 at 19:44
  • Leaving out important details makes it difficult to simplify :P With that description, you likely will not be able to simplify it much more than it already is. – Zac Howland Dec 21 '10 at 19:48
  • I have the same opinion... But the problem is so complicated, it can not be described in detail in one paragraph. Some details will appear later in discussion :-). – Ian Dec 21 '10 at 20:01