Template Class - coding style

Pages: 12
I completely disagree with the blog.

The #pragma once directive is a very wonderful thing, when the compiler toolchain supports it, but it is the pragma directive that is fragile, not the include guards.

The problem lies with the filesystem. It is entirely possible to provide the same physical file to the preprocessor in such a way as to be impossible or impractical to recognize as the same exact file. Likewise, it is possible to have two files, in two separate locations, which are actually copies of each other -- either byte by byte or in some way reformatted. (This can and does happen! Sometimes it takes years before someone notices and fixes the error in CVS/SVN/repository.) The pragma once directive is powerless to defeat such inputs.

The include guards will always work when appropriately named. If you are concerned with name collisions, especially because you named your file something like "strings.h" or some like high-collision value, naming your include macro "STRINGS_H" is not just foolhardy, but positively asking for trouble.

Use a solid convention for unique names. A good one is, for example, just a date-time stamp along with the filename. I always add the library name as well. The odds for collision here are infinitesimal.
1
2
#ifndef SPIFF_STRINGS_20110115_2219Z_HPP
#define SPIFF_STRINGS_20110115_2219Z_HPP 

Many compilers are also optimized to recognize include guards and have the same effect as using pragma once. GCC is one such toolchain.


A good practice is to use both pragma once and include guards. Those compilers which optimize on pragma once/include guard filename recognition will work nicely. When they fail, or if the compiler doesn't optimize on filename recognition, then the include guard will catch it.

─────────────────────────

For core, integral/numeric types you don't need a reference, as just making a copy is just as fast/secure/efficient/etc. Hence, there is no need to say const float& x over float x. Where copying is prohibitive (that is, expensive), like std::string s would be, or if you don't know the type, as in templated classes, then you want to use a const reference const std::string& s.

─────────────────────────

@barliesque
I'm not sure, but it is probably because your copy constructor assumes a different templated type?

Hope this helps.
I thought that might be it as well, so I added another copy constructor with a single type, and got a massive list of compile errors -- Difficult to see what it was really complaining about. I'll post again with some better detail.
Coming back to this, I've rewritten my template class in a different manner. Everything seems to be working perfectly, apart from one thing. In order to identify different instances of my class, I'm using an id property that's automatically assigned from a static counter. At first I was a little thrown when I saw my test code produce instances numbered: 0, 1, 2, 0. Why another zero instead of 3??? ...well, I quickly realized that because the first three instances were of type Rectangle<int> and the last was Rectangle<float>, I have *two* static counters!

So, my question is, how might I code a static counter that is applied to all variants of Rectangle<>?

Further to the subject of template coding style, I'm really liking the following approach. I've tried to take everyone's advice to heart, and hope that this coding style is clear to all:

Rectangle.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#ifndef RECTANGLE_H
#define RECTANGLE_H

#include <iostream>
#include <string>
#include <sstream>


template <class N>
class Rectangle
{
	
	/********************
	*    PROPERTIES
	*********************/
	public:
		N x,y,width,height;
	
	protected:
		unsigned int _id;

	private:
		static unsigned int _counter;

	/********************
	*    CONSTRUCTORS
	*********************/
	public:
		Rectangle(N x, N y, N width, N height)
		:	x(x), y(y), width(width), height(height), _id(Rectangle<N>::_counter++)
		{
			std::cout << "CONSTRUCT Rect(x,y,w,h) ID: " << _id << std::endl;
		}

		Rectangle(N width, N height)
		:	x(0), y(0), width(width), height(height), _id(Rectangle<N>::_counter++)
		{
			std::cout << "CONSTRUCT Rect(w,h) ID: " << _id << std::endl;
		}

		Rectangle()
		:	x(0), y(0), width(0), height(0), _id(Rectangle<N>::_counter++)
		{
			std::cout << "CONSTRUCT Rect() ID: " << _id << std::endl;
		}
		
		Rectangle(const Rectangle<N>& rect)
		:	x(rect.x), y(rect.y), width(rect.width), height(rect.height), _id(Rectangle<N>::_counter++)
		{
			std::cout << "CONSTRUCT Rect(Rectangle<>) ID: " << _id << std::endl;
		}
		
		template <class N2>
		Rectangle(const Rectangle<N2>& rect)
		:	x(rect.x), y(rect.y), width(rect.width), height(rect.height), _id(Rectangle<N>::_counter++)
		{
			std::cout << "CONSTRUCT Rect(Rectangle<N2>) ID: " << _id << std::endl;
		}
		
		Rectangle<N>& operator= (const Rectangle<N>& rect) {
			x = rect.x;
			y = rect.y;
			width = rect.width;
			height = rect.height;
			std::cout << "ASSIGNMENT operator= ID: " << _id << std::endl;
		}
		
		template <class N2>
		Rectangle<N>& operator= (const Rectangle<N2>& rect) {
			x = rect.x;
			y = rect.y;
			width = rect.width;
			height = rect.height;
			std::cout << "CAST ASSIGNMENT operator= ID: " << _id << std::endl;
		}


	/********************
	*    DESTRUCTOR
	*********************/
	public:
		virtual ~Rectangle() {
			std::cout << "DESTRUCT Rect ID: " << _id << std::endl;
		}

	/********************
	*    METHODS
	*********************/
	public:

		const std::string toString() {
			std::stringstream ss (std::stringstream::in | std::stringstream::out);
			ss << "Rectangle[id:" << _id << ", x:" << x << ", y:" << y << ", width:" << width << ", height:" << height << "]";
			return ss.str();
		}
		

	/***********************
	*    GETTER / SETTERS
	************************/
	unsigned int const& id() const { return _id; }
	/* READ ONLY
	unsigned int const& id(unsigned int value) { return _id = value; }
	*/
	
};	// class Rectangle<N>


/********************************
*    DEFINE STATIC VARIABLES
********************************/

template <class N>	unsigned int Rectangle<N>::_counter = 0;



#endif // RECTANGLE_H 
So, my question is, how might I code a static counter that is applied to all variants of Rectangle<>?


Here is one method: (putting the static variale(s) in a common base class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#ifndef RECTANGLE_H
#define RECTANGLE_H

#include <iostream>
#include <string>
#include <sstream>

//Common Base Class
class countx
{
    protected:
         static unsigned int _counter;
       
};   


template <class N>
class Rectangle :public countx //common base class
{
    
    /********************
    *    PROPERTIES
    *********************/
    public:
        N x,y,width,height;
        //static unsigned int _counter;
            
    protected:
        unsigned int _id;

    private:
        //static unsigned int _counter;

    /********************
    *    CONSTRUCTORS
    *********************/
    public:
        Rectangle(N x, N y, N width, N height)
        :    x(x), y(y), width(width), height(height), _id(Rectangle<N>::_counter++)
        {
            std::cout << "CONSTRUCT Rect(x,y,w,h) ID: " << _id <<  std::endl;
        }

        Rectangle(N width, N height)
        :    x(0), y(0), width(width), height(height), _id(Rectangle<N>::_counter++)
        {
            std::cout << "CONSTRUCT Rect(w,h) ID: " << _id << std::endl;
        }

        Rectangle()
        :    x(0), y(0), width(0), height(0), _id(Rectangle<N>::_counter++)
        {
            std::cout << "CONSTRUCT Rect() ID: " << _id << std::endl;
        }
        
        Rectangle(const Rectangle<N>& rect)
        :    x(rect.x), y(rect.y), width(rect.width), height(rect.height), _id(Rectangle<N>::_counter++)
        {
            std::cout << "CONSTRUCT Rect(Rectangle<>) ID: " << _id << std::endl;
        }
        
        template <class N2>
        Rectangle(const Rectangle<N2>& rect)
        :    x(rect.x), y(rect.y), width(rect.width), height(rect.height), _id(Rectangle<N>::_counter++)
        {
            std::cout << "CONSTRUCT Rect(Rectangle<N2>) ID: " << _id << std::endl;
        }
        
        Rectangle<N>& operator= (const Rectangle<N>& rect) {
            x = rect.x;
            y = rect.y;
            width = rect.width;
            height = rect.height;
            std::cout << "ASSIGNMENT operator= ID: " << _id << std::endl;
        }
        
        template <class N2>
        Rectangle<N>& operator= (const Rectangle<N2>& rect) {
            x = rect.x;
            y = rect.y;
            width = rect.width;
            height = rect.height;
            std::cout << "CAST ASSIGNMENT operator= ID: " << _id << std::endl;
        }


    /********************
    *    DESTRUCTOR
    *********************/
    public:
        virtual ~Rectangle() {
            std::cout << "DESTRUCT Rect ID: " << _id << std::endl;
        }

    /********************
    *    METHODS
    *********************/
    public:

        const std::string toString() {
            std::stringstream ss (std::stringstream::in | std::stringstream::out);
            ss << "Rectangle[id:" << _id << ", x:" << x << ", y:" << y << ", width:" << width << ", height:" << height << "]";
            return ss.str();
        }
        

    /***********************
    *    GETTER / SETTERS
    ************************/
    unsigned int const& id() const { return _id; }
    /* READ ONLY
    unsigned int const& id(unsigned int value) { return _id = value; }
    */
    
};    // class Rectangle<N>


/********************************
*    DEFINE STATIC VARIABLES
********************************/
unsigned int countx::_counter;


#endif // RECTANGLE_H  




Testing, testing,
1
2
3
4
5
6
7
8
9
int main()

{
   
 Rectangle<int> r1; 
  Rectangle<int> r2, r3; 
 Rectangle<float> r4;
  Rectangle<double> r5;
}
Excellent. Thank you. Seems so obvious now.

Better yet, I've moved _id, its assignment and its getter into the base class as well.

Thank you.
Topic archived. No new replies allowed.
Pages: 12