Original Post
What functions would a good 2D & 3D vector class have? I plan on making 2 seperate classes for each. Dot, Normalize etc?
Quote:
Original post by hymerman
Apparently, none: http://www.gamedev.net/community/forums/topic.asp?topic_id=506184
Quote:
My advice would be to code things as and when you need them, since you never know what will be needed and what will just be a waste of time.
Quote:
Original post by Ravyne Quote:
Original post by hymerman
Apparently, none: http://www.gamedev.net/community/forums/topic.asp?topic_id=506184
If that's what you took away from that thread, then you're really misinterpreted what others have said.
A 'class' is two things -- data and interface. The interface is made up of both member and non-member functions, and in fact, non-member functions are usually preferable. Just because a function is defined outside of the class construct doesn't make it any less a part of the class (With the caveat that it must be defined in the same namespace as the class in order for Koenig look-up to be able to find it). I generally preserve member functions for functions which either are not implementable with the public interface (They have to be) or those which modify the class's state (which may have to be in the future, and so, should be now).Quote:
My advice would be to code things as and when you need them, since you never know what will be needed and what will just be a waste of time.
For example, I've seen a couple C++ vector classes which overload some combination of *, % and ^ to indicate the various products associated with vectors. The problem in using the % and ^ operators is that they have even lower precedence than the + and - operators and can lead to code like this:
a % b + c ^ d // ab c d
which, one would naively assume, would be evaluated as (a%b)+(c^d), but instead is evaluated as a%(b+c)^d. All this is a result of poorly choosing operators to overload.
Quote:
Original post by Ravyne
If that's what you took away from that thread, then you're really misinterpreted what others have said.
Quote:
A 'class' is two things -- data and interface. The interface is made up of both member and non-member functions, and in fact, non-member functions are usually preferable. Just because a function is defined outside of the class construct doesn't make it any less a part of the class
Quote:
Quote:
My advice would be to code things as and when you need them, since you never know what will be needed and what will just be a waste of time.
This is a re-statement of the YAGNI, or You Ain't Gonna Need It, principle. Unfortunately, IMO, some YAGNI proponents take it too far, or oversimplify when extolling its virtues to newbies.
Quote:
Original post by johnstanp Quote:
Original post by Ravyne Quote:
Original post by hymerman
Apparently, none: http://www.gamedev.net/community/forums/topic.asp?topic_id=506184
If that's what you took away from that thread, then you're really misinterpreted what others have said.
A 'class' is two things -- data and interface. The interface is made up of both member and non-member functions, and in fact, non-member functions are usually preferable. Just because a function is defined outside of the class construct doesn't make it any less a part of the class (With the caveat that it must be defined in the same namespace as the class in order for Koenig look-up to be able to find it). I generally preserve member functions for functions which either are not implementable with the public interface (They have to be) or those which modify the class's state (which may have to be in the future, and so, should be now).Quote:
My advice would be to code things as and when you need them, since you never know what will be needed and what will just be a waste of time.
For example, I've seen a couple C++ vector classes which overload some combination of *, % and ^ to indicate the various products associated with vectors. The problem in using the % and ^ operators is that they have even lower precedence than the + and - operators and can lead to code like this:
a % b + c ^ d // ab c d
which, one would naively assume, would be evaluated as (a%b)+(c^d), but instead is evaluated as a%(b+c)^d. All this is a result of poorly choosing operators to overload.
Yes, good advice!!!
It can be a painful process to realize that a very long formula( like torque computing in 3D, in order to have a rigid body in a desire state ) doesn't produce the expected result because of the precedence of some operators on the others...
Defining a "const Vector3 cross( const Vector3& )const" instead of "const Vector3 operator^( const Vector3& )const", adds moreover clarity to the code, and doesn't require familiarity with the interface.
Vector3& Vector3::operator+=(const Vector3& rhs){ X += rhs.X; Y += rhs.Y; Z += rhs.Z; return *this;}Vector3 operator+(const Vector3& lhs, const Vector3& rhs){ Vector3 temp(lhs); return temp += rhs;}Vector3& operator+=(Vector3& lhs, const Vector3& rhs){ lhs.X += rhs.X; lhs.Y += rhs.Y; lhs.Z += rhs.Z; return *this;}Vector3 operator+(Vector3 lhs, const Vector3& rhs){ return lhs += rhs;}Quote:
Original post by Ravyne
Note that, in this version the += operator is defined as a non-member function as given in the book "C++ Coding Standards" by Sutter and Alexandrescu. I'm not actually certain that this is required in order to reap the benefits of the potential optimization, since the member form, AFAIK, receives a pointer to a non-const Vector3, this, which ought to be equivalent to the non-const reference as far as optimization is concerned.
Quote:
Original post by Ravyne
You're welcome.
Careful though -- There's no reason for the cross (or other products) function to be a member since it should never change the state of either argument it takes. The same applies to the standard non-assigning mathematical operators (+, -, *, /, which should all be implemented in terms of their assigning counterparts, which should be members) as well. Remember, if it doesn't *have* to be a member function, then it *shouldn't* be a member function -- always default to non-member first. Note also, that the return value should not be const. It would look like:
Vector3 cross(const Vector3& lhs, const Vector3& rhs)
typedef RVector3 Matrix<1, 3>;typedef CVector3 Matrix<3, 1>;RVector3 dot(const RVector3 & lhs, const RVector3 & rhs) { return lhs * rhs.transpos();}Matrix<4, 4> translate(float x, float y, float z) { Matrix<4, 4> result = Matrix<4, 4>::Identity(); result(3, 0) = x; result(3, 1) = y; result(3, 2) = z; return result;}Matrix<4, 4> m = rotateX(90) * rotateY(90) * translate(1, 2, 3);Quote:
Original post by SiCrane Quote:
Original post by Ravyne
Note that, in this version the += operator is defined as a non-member function as given in the book "C++ Coding Standards" by Sutter and Alexandrescu. I'm not actually certain that this is required in order to reap the benefits of the potential optimization, since the member form, AFAIK, receives a pointer to a non-const Vector3, this, which ought to be equivalent to the non-const reference as far as optimization is concerned.
If you're referring to Item 27 in "C++ Coding Standards" re-read that section. The reason for using a non-member operator@= is because of Item 44, not optimization, as noted on the bottom of page 48. The optimization comment is referring to operator@ taking the first argument by value as mentioned at the top of page 49.
Quote:
Original post by johnstanp Quote:
Original post by Ravyne
You're welcome.
Careful though -- There's no reason for the cross (or other products) function to be a member since it should never change the state of either argument it takes. The same applies to the standard non-assigning mathematical operators (+, -, *, /, which should all be implemented in terms of their assigning counterparts, which should be members) as well. Remember, if it doesn't *have* to be a member function, then it *shouldn't* be a member function -- always default to non-member first. Note also, that the return value should not be const. It would look like:
Vector3 cross(const Vector3& lhs, const Vector3& rhs)
I think it's simply a matter of taste: the only( one of the ) benefit(s) of using a non-member function is allowing the compiler to make type conversion( via constructors ). I prefer defining all operators as member functions to stay close to the Object Oriented Paradigm...I simply believe that defining everything( operators included ) in the class, allows me to prevent temporary copies( type conversions ) by enforcing all my constructors to be explicit...
I only define non member functions for operators that cannot be implemented as member functions...I believe defining everything in the class is a good habit and eases the reimplementation of the code in another language, stricter( good grammar? ) on the OOP point of view.
And for operator overloading( +, -, *, .. ), I only use it for the clarity of the code and debugging ease: it's not essential after all...
Quote:
For the constant return value: it is simply a way to avoid writing less readable code. I don't see the necessity of being able to write the following code:
v3 = v1.cross( v2 ).normalize();
I prefer:
v3 = v1.cross( v2 );
v3.normalize();
Its simply a matter of taste.
Quote:
Original post by Ravyne
While it's not the worst thing you can do, its not really a matter of taste or opinion, and its certainly not "stricter on the OOP point of view" because it is, in fact, quite the opposite. Non-member, non-friend functions are not allowed to access the private members of the class to which they pertain; they must be implemented using the same public interface everyone is presented with. This *improves encapsulation* and *reduces coupling*, and its practice serves to make classes less monolithic -- All good things from the "OOP point of view".
Quote:
If the function (rightly) requires access to the class's internals, then make it a member function. According to the experts, you've got things backwards -- You should only make the functions a member when you have not, not only make functions a non-member when you have to.
template< typename Real, unsigned int N >class Vector{ public: Vector() : elem_() {} Real operator[]( unsigned int i )const { if( i < N ) { return elem_; } else { //throw an exception; } } Real length()const { Real sum = 0.0; for( int i = 0; i < N; ++i ) { sum += elem_ * elem_; } return sqrt( sum ); } } private: Real elem_[N];};#include "Vector.hpp"template< typename Real, unsigned int N >Real length( const Vector< Real, N >& vector ){ Real sum = 0.0; for( int i = 0; i < N; ++i ) { sum += vector * vector; } return sqrt( sum );}Quote:
Quote:
For the constant return value: it is simply a way to avoid writing less readable code. I don't see the necessity of being able to write the following code:
v3 = v1.cross( v2 ).normalize();
I prefer:
v3 = v1.cross( v2 );
v3.normalize();
Its simply a matter of taste.
I used to subscribe to the same line of thinking, however, I've since stopped trying to babysit the programmers making use of my code (most often, myself). Just because you may not like the first line of code, others (myself included, in this instance) might see nothing wrong with it, or even prefer it. As someone who provides code to others to use (even if only just yourself) it is your job to help them get things done, not to tell them how to to their job "The Right Way" (TM). When wearing your "API provider" hat, you must have faith that your clients won't intentionally sabotage themselves. The crime you're committing by enforcing the const return value is in reducing the expressiveness of the code you've provided.
Quote:
I ask, what would happen if you changed to a non-const return value? You're preferred code would still work, correct? There is no additional overhead, correct? So, what is it that you gain by adding const? Nothing. It doesn't affect, at all, your preferred way of doing things. What do your clients (even, perhaps, your future self) lose by being forced to bear the burden of a const return? They loose the ability to express their code in the way they see fit. You're taking something away from them, and gaining nothing for yourself. You're forcing your opinions upon them. Its simply The Wrong Thing (TM) to do -- and while this is my own opinion, I believe the more learned members of this forum would agree.
Quote:
Beyond all of this "API morality" speak, it violates the cardinal fall-back of class design -- When in doubt, do as the ints do. The ints and their mathematical operators all return non-const, which allows some weird and seemingly useless lines of code, however, this is a known and accepted part of the language. To go against this is to go against the Principle of Least Surprise and can only lead to headaches for your clients.
Quote:
Original post by MrPickle
What functions would a good 2D & 3D vector class have?
I plan on making 2 seperate classes for each.
Dot, Normalize etc?
Quote:Careful, there... removing freedom from your clients can actually be a blessing (for them) if it prevents them from doing stupid things, and many experienced programmers go to great lengths to restrict their own freedom. That's the entire reason things like a type system or private variables exist. The actual question is not whether it is moral to prevent your clients from doing things as they like, but rather to know if the provided freedom allows a frequent error to happen (so that it would deserve to be removed).
Original post by Ravyne
I ask, what would happen if you changed to a non-const return value? You're preferred code would still work, correct? There is no additional overhead, correct? So, what is it that you gain by adding const? Nothing. It doesn't affect, at all, your preferred way of doing things. What do your clients (even, perhaps, your future self) lose by being forced to bear the burden of a const return? They loose the ability to express their code in the way they see fit. You're taking something away from them, and gaining nothing for yourself. You're forcing your opinions upon them. Its simply The Wrong Thing (TM) to do -- and while this is my own opinion, I believe the more learned members of this forum would agree.
Quote:Ah, the classic (pun intended) mix between "put everything in classes" and "object oriented programming". I'm quite sorry to disappoint you, but neither implies the other.
Original post by johnstanp
In object oriented, everything must be defined inside a class...C++ is not totally object oriented because it offers the possibility of procedural programming you've just defined( non-member functions )...You cannot for instance declare a function in Java outside of a class. If you try reimplementing your C++ code that makes heavy use of non member function in an Object Oriented Language, that doesn't allow procedural programming, you'll understand why C++ is not a pure Object Oriented Programming Language.
The only non-member functions I define are friend functions( for speed issues in game programming ): they are in fact an extension of the interface of my classes, since they depend of its implementation.
window # add_button object method onClick = print_endline "Button was clicked" endOr, of course, object-oriented languages which support runtime decoration of objects with additional methods. But this is a minor nitpick, as I suspect you meant to "put everything into objects" (because adding things to classes as opposed to objects implies static behavior, which is procedural by its very nature). Quote:
Original post by Dtag
The downside of using non-member functions is obviously that you get a non-uniform interface for the class. I.e you may have to write
yourclass.someOperation() for one thing and someOtherOperation(yourclass). Iam quite sure that people wouldnt be so much 'offended' by the latter notation if it wasnt so different to the first one.
This topic has been locked by a moderator. New replies are not allowed.
With your permission, GameDev.net uses analytics cookies to understand how people use the platform. You can accept analytics or continue with necessary cookies only. Learn more