Requesting peer review of 3D Vector class.

Started by
53 comments, last by jpetrie 15 years, 9 months ago
Anonymous unions are ok in C++. Anonymous structs however are not. :)

The Microsoft Visual C++ warning for this does mention something along the lines of non-standard extension used: anonymous struct/union. Though it doesn't actually emit this message for anonymous unions.
Advertisement
Those uses of unions look really nice, but I don't see how make it easy to use unless I really did bow down to the idea that I should use sets and gets (so it wouldn't become harder to access the x y and z).

Quote:Original post by ToohrVyk
Quote:Original post by Splinter of Chaos
Because it's not simpler or easier to USE.
vect = normalize( vect ); 
is so much more typing than
vect.normalize();
and doesn't get the point across any better, and doesn't leave me the option of writing a non-member normalize that doesn't alter the data.


How kind of you to give that precise example I was expecting.
std::transform(vectors.begin(), vectors.end(), vectors.begin(), &normalize);



I'm sorry, I have no friken clue what you're doing.

[ EDIT ]
Oh, I get it. Assuming there's an STL something of vectors, this is an example of...well, I don't get what you're trying to prove. Having a copy of normalize() outside the class and have it not modify the class itself has been agreed on, and that's the only point I can see you trying to make.
[ /EDIT ]

Quote:Original post by jpetrie
Quote:
Because it's not simpler or easier to USE.

vect = normalize( vect );

is so much more typing than

vect.normalize();

and doesn't get the point across any better, and doesn't leave me the option of writing a non-member normalize that doesn't alter the data.

Be careful here. As a programmer, keystrokes are trivial, you should not use keystroke count to gate the simplicity or usability of code -- it's roughly akin to judging a book by it's cover; pure superficiality. If four or five characters is really that much of a deciding factor, you might want to consider a new hobby, because you're going to be typing a lot more than that.


I also forgot to mention it was less efficient because non-optimizing compilers (and for all I know, optimizing ones) will be forced to create a copy, the send the data back to the original, then delete the copy when it goes out of scope. And, my point wasn't it made it MORE user-friendly, but it didn't make it LESS.

But, more to the point: I have to add the extra keystrokes to EVERY use and the number I have to add is a function of how long the vector's name is. What if I feel the need to call a vector position? Velocity? In practice, because I was already tired of writing position once, I shortened it to pos. I'd have really been annoyed if I'd made a system where I had to write it twice!

Quote:Original post by jpetrie
However, when you say that the non-member option doesn't give you the option of a non-member, non-modifying version -- that doesn't make sense, since that's exactly what the non-member option is. Did you mean to say something else?


Probably. I was extremely tired when I posted that. I think I meant I can't use the same function name, one that alters the core data and one that doesn't, by having one be a class function and the other being a help function if I make it a point to never have one IN the function.
Just for the record, the encapsulation of data isn't because "hiding data" is good OOP. It's to do with exposing an interface of the object (a black box). For a great many (99%+) objects you'll create, not all the data members are required by whoever uses your code. Thus, you have over-exposed if you leave them public. You want to expose the minimum workable interface to support 100% functionality. It's about behaviour, not about hiding things. Leaving x and y public is fine, because the behaviour that implies (modifying and reading from those scalars) is exactly what the class is all about. If you wanted something to be read-only, leaving it as public is bad, since you're exposing more behaviour than your interface intends (ie, people can modify it).

This is why trivial accessors and mutators (getters/setters) are looked upon with disdain (just make it public! same behaviour), unless the codebase is volatile.
[ search: google ][ programming: msdn | boost | opengl ][ languages: nihongo ]
[deleted]
So why are you suggesting that SOC writes his C++ vector3d class without member functions... Use C if you want to write like C. One of the most useful things about classes is when your IDE (such as VisualStudio) tells you what your class can do. Searching around for what a class can do simply because you've made them plain C-like functions is a pain. The whole point of writing a C++ vector3 in a CLASS is to give it functionality.

And, yes, do use templates. What if you want to use a vector of doubles for added precision in part of your program, but also need to use float vectors? Its not often you come across a non-template vector3 class.

this is my vector3 implementation (not all of it though)
#include <cmath>// Template 3D Vectortemplate <class T>class vector3{public:	// for something as trivial as three coordinates you DON'T need accessor functions - that's overkill	T x, y, z;	// Constants	static vector3 zero;	// default constructor (does nothing, use ::zero for a zero vector)	vector3 () { }	// coordinate constructor (use an initialiser list)	vector3 (T vx, T vy, T vz) : x (vx), y (vy), z (vz) { }	// copy constructor	vector3 (const vector3& other) : x (other.x), y (other.y), z (other.z) { }	// gets the squared length (squared magnitude)	T length_sqr (void) const	{		return x * x + y * y + z * z;	}	// gets the length (magnitude)	T length (void) const	{		return sqrt (length_sqr ());	}	// sets the length (magnitude)	void set_length (T length)	{		normalise ();		*this *= length;	}	// normalises the vector	void normalise (void)	{		T len = length ();		ASSERT (len != 0);		*this /= len;	}	// gets the unit vector of this vector	vector3 get_unit (void) const	{		vector3 unit = *this;		unit.normalise ();		return unit;	}	// gets the dot product	T dot_product (const vector3& other) const	{		return x * other.x + y * other.y + z * other.z;	}	// negate operator	vector3 operator - (void) const	{		return vector3 (-x, -y, -z);	}	// vector arithmetic operators	vector3 operator + (const vector3& rhs)	{ return vector3 (x + rhs.x, y + rhs.y, z + rhs.z); }	vector3 operator - (const vector3& rhs)	{ return vector3 (x - rhs.x, y - rhs.y, z - rhs.z); }	vector3 operator * (const vector3& rhs)	{ return vector3 (x * rhs.x, y * rhs.y, z * rhs.z); }	vector3 operator / (const vector3& rhs)	{ return vector3 (x / rhs.x, y / rhs.y, z / rhs.z); }	// vector assignment arithmetic operators	vector3& operator += (const vector3& rhs)	{ x += rhs.x; y += rhs.y; z += rhs.z; return *this; }	vector3& operator -= (const vector3& rhs)	{ x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; }	vector3& operator *= (const vector3& rhs)	{ x *= rhs.x; y *= rhs.y; z *= rhs.z; return *this; }	vector3& operator /= (const vector3& rhs)	{ x /= rhs.x; y /= rhs.y; z /= rhs.z; return *this; }	// value arithmetic operators	vector3 operator + (T rhs)	{ return vector3 (x + rhs, y + rhs, z + rhs); }	vector3 operator - (T rhs)	{ return vector3 (x - rhs, y - rhs, z - rhs); }	vector3 operator * (T rhs)	{ return vector3 (x * rhs, y * rhs, z * rhs); }	vector3 operator / (T rhs)	{ return vector3 (x / rhs, y / rhs, z / rhs); }	// value assignment arithmetic operators	vector3& operator += (T rhs)	{ x += rhs; y += rhs; z += rhs; return *this; }	vector3& operator -= (T rhs)	{ x -= rhs; y -= rhs; z -= rhs; return *this; }	vector3& operator *= (T rhs)	{ x *= rhs; y *= rhs; z *= rhs; return *this; }	vector3& operator /= (T rhs)	{ x /= rhs; y /= rhs; z /= rhs; return *this; }};// Initialise ZERO vectortemplate <class T> vector3<T> vector3 <T>::zero (0, 0, 0);// Typestypedef Vector3 <float> vector3f;typedef Vector3 <double> vector3d;typedef Vector3 <int> vector3i;


in use
vector3f pos = vector3f::zero;vector3f diff = (target - pos);vector3f diff_unit = diff.get_unit ();printf ("distance to target: %f\n", diff.length ());vector3f velocity (2, 1.4, 0);pos += velocity * time_delta;


The whole point of having a C++ class implementation of a vector is to have member functionality that pertains to A) what the vector as a type of object can do and B) because C++ classes were designed to be able to do this.
Quote:So why are you suggesting that SOC writes his C++ vector3d class without member functions... Use C if you want to write like C.
Quote:The whole point of writing a C++ vector3 in a CLASS is to give it functionality.
Quote:The whole point of having a C++ class implementation of a vector is to have member functionality that pertains to A) what the vector as a type of object can do and B) because C++ classes were designed to be able to do this.
Recommended reading.

Also, consider that the SC++L itself includes many free functions that interact with objects via their public interfaces (usually in the form of iterators).

There are other circumstances as well where non-member functions can be useful or convenient. Just as an example, some find this:
float l = length(v1 - v2);
Easier to read and/or write than this:
float l = (v1 - v2).length();
In short, I'm not sure that your argument - which I read as 'if it's possible to make it a member function, do so, because that's what classes in C++ are for' - is sound.
I like using C++ member functions and encapsulation because my C++ IDE plays nice with them and makes writing code easier.

vect = normalize(vect), is actually longer to type:

"vect" "=" "norm" Ctrl+Space "v" Ctrl + Space

versus

"vec" Ctrl+Space ".nrm" Ctrl+Space


you guys might think that's a silly reason, but it speeds me up, reduces errors, and saves my RSI from flaring up and to me that's enough to justify it [smile]
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:Original post by silvermace
I like using C++ member functions and encapsulation because my C++ IDE plays nice with them and makes writing code easier.


Don't confuse encapsulation with "stuffing things into a class as members". It's perfectly possible to improve encapsulation without being a member and be a member without improving encapsulation.

Quote:vect = normalize(vect), is actually longer to type:


Nobody argued that it's shorter to type. What everyone has been arguing is that 1° speed of typing is a negligible factor when compared to the advantages provided by the improved encapsulation and interaction with language features and 2° this kind of operation doesn't happen all that often in code (if it happens, why don't you refactor? there are not that many operations in 3D that require a normalization, anyway).

Quote:Original post by Splinter of Chaos
Quote:std::transform(vectors.begin(), vectors.end(), vectors.begin(), &normalize);

I'm sorry, I have no friken clue what you're doing.


I'm sorry, but if you don't know what std::transform does, you certainly don't have the experience and knowledge necessary to judge what is and what isn't a good real-world C++ practice. Sure, you can write code using the subset of C++ that you do know, and you can even decide on "best practices" that work well with your subset of C++, but don't expect these to cross over to the real world of industry programming.

In short, if you never use the Standard C++ Library (which is a bad practice), then it makes no difference to use a member function or a non-member function in a vector class, and whatever you choose to do will be a good practice as long as you keep it consistent. If you work on a real-world project that involves the SC++L, using a member function where a non-member function is a possibility is a bad practice, because it makes that function harder to use in conjunction with standard library algorithms.
Quote:Original post by ToohrVyk
Don't confuse encapsulation with "stuffing things into a class as members". It's perfectly possible to improve encapsulation without being a member and be a member without improving encapsulation.


I never said it wasn't, and I never said the C-style method was wrong or bad, I simply said I prefer member-functions over the alternative C-style proposition. I believe this is a valid position as we both agree good encapsulation doesn't just mean member-functions.

I did actually mean encapsulation; admittedly my example was too simple to convey any decent signs of good encapsulation. A better example is implementing interfaces. A decent IDE saves you significant time in implementing interfaces or extending classes through validating the contract between an interface implementation and its usage (though relatively trivially compared to what a human could verify, it does remove common errors form the users concerns) additionally, a good HLL (e.g. Java) IDE should be able to assist very well with test generation/and-or reuse for well encapsulated code, again saving significant time (hence not just in coding time, but the whole dev cycle).

While it is very possible to write well encapsulated C-style code, you would be hard pressed to find an IDE which would actually improve your productivity with C-style encapsulation over the more common OO C++/Java style encapsulation.

[EDIT: I also agree with ToohrVyk that if you don't know what a simple std::transform that you lack the experience and knowledge necessary to judge what is and what isn't a good real-world C++ practice]
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
Quote:Original post by ToohrVyk
Quote:Original post by Splinter of Chaos
Quote:std::transform(vectors.begin(), vectors.end(), vectors.begin(), &normalize);

I'm sorry, I have no friken clue what you're doing.


I'm sorry, but if you don't know what std::transform does, you certainly don't have the experience and knowledge necessary to judge what is and what isn't a good real-world C++ practice.


You miss quoted me. Seeing Vector, but have you actually mean vector as in array, was what confused me. You conveniently left out my [ EDIT ], which I will now reprint:

Quote:Original post by Splinter of Chaos
I'm sorry, I have no friken clue what you're doing.

[ EDIT ]
Oh, I get it. Assuming there's an STL something of vectors, this is an example of...well, I don't get what you're trying to prove. Having a copy of normalize() outside the class and have it not modify the class itself has been agreed on, and that's the only point I can see you trying to make.
[ /EDIT ]


By miss quoting me, you not only undersold me, but followed it up with an insult. Instead of encouraging me to learn the STL (which I DO already know), you berated me for not knowing it, which does not encourage growth. Besides which, there are people who don't use them--one reason or another--and that doesn't make me, or you, a better C++ programmer than them.

This topic is closed to new replies.

Advertisement