Sign in to follow this  
beebs1

Which do you prefer?

Recommended Posts

beebs1    398
Hiya,

I've seen all of these 'styles' used for mathematical operations. Could anyone tell me which you prefer, and why?

[CODE]
class Vector3
{
// This?
// Equivalent to a free function.
void normalise();

// Or this?
Vector3& normalise();

// Or this?
Vector3 normalise() const;
}
[/CODE]

The first is limiting as you can't use it as part of an equation, but it's unlikely to be confusing. It seems the second and third varieties are more flexible, but could easily be confused, e.g.

[CODE]
// Should v1 be modified here?
Vector3 v1;
Vector3 v2 = v1.normalise();
[/CODE]

Which do you use, or which do you prefer? Thanks for any suggestions! Edited by Telios

Share this post


Link to post
Share on other sites
TheUnnamable    1129
I'd prefer Vector3& normalise(); as it's part of the class, meaning that it actually changes the instance. Also, this returns a reference to the instance itself, so you can use more functions on it in a row.
For your second line, I'd create a global function outside the class, that would return a new instance, without changing the original one:
[code]class Vector3;
Vector3 Normalise(const Vector3&);[/code]

This way it makes sense for me, although that global function is still a bit problematic, spamming the global namespace. An alternative could be this:
[code]
class Vector3
{
Vector3& Normalise(void); //<- Changing the original instance, then returning itself
Vector3 Normalised(void); //<- Returning a normalised copy of the original one
};

Vector3 v1;
Vector3 v2=v1.Normalised();
[/code]
Ta-dah!

Share this post


Link to post
Share on other sites
Muzzy A    737
I agree with alvaro, free function with the Correct spelling lol. It's how I do all my linear algebra. But at the same time, I am totally obsessed with optimization that even having that extra copy in there urks me sometimes. But i don't do it just because I have an unknown reason against changing a variable passed in a function by reference.

grah you have me thinking about changing all my code lol. Edited by Muzzy A

Share this post


Link to post
Share on other sites
beebs1    398
Thanks for that! I think I'm leaning towards free functions too, but then I'd feel I need to make [i]everything[/i] a free function for consistency. Some operations sit fine with me as const member functions - the dot product for example, or the determinant of a matrix. So I'll end up with:

[CODE]
float Vector3Dot(const Vector3& v1, const Vector3& v2);
Vector3 Vector3Normalise(const Vector3& v1);

float Matrix3x3Determinant(const Matrix3x3& m);
// etc...
[/CODE]

I believe that's how D3DX and it's newer version (XNA Math?) do it. Another alternative is to use free functions which modify the instance passed in rather than return a new one. That way you only pay for a copy if you need it:

[CODE]
void Vector3Normalise(Vector3& v) { /*..*/ }

Vector3 v1;
// Modify the original - no copy.
Vector3Normalise(v1);
// Don't modify the original - copy.
Vector3 v2 = v1;
Vector3Normalise(v2);
[/CODE]

Although such copies are unlikely to be a bottleneck.

[quote name='Muzzy A' timestamp='1338136330' post='4943742']
...free function with the Correct spelling lol.
[/quote]

I'm not even gonna go there [img]http://public.gamedev.net//public/style_emoticons/default/tongue.png[/img] Edited by Telios

Share this post


Link to post
Share on other sites
Bacterius    13165
I like class functions a lot but they tend to make equations messy in code. Free functions with a return value are much more intuitive in my opinion. For instance to calculate 3x^2 + 2:

Free function with no return value:

[CODE]
square(x);
multiply(x, 3);
add(x, 2);
return x;
[/CODE]

Class function:

[CODE]
x.square();
x.multiply(3);
x.add(2);
return x;
[/CODE]

Or, if the class function returns itself:

[CODE]return x.square().multiply(3).add(2);[/CODE]

Free function with return value:

[CODE]return 3 * x * x + 2; // or return 3x**2 + 2 if ** is supported[/CODE]

Of course this is a trivial example considering the free function is in fact a language operator. Slightly more complicated: calculate normalize(cross(v1, v2)):

Free function with no return value:

[CODE]
cross(v1, v2); // modifies v1
normalize(v1);
return v1;
[/CODE]

Class function:

[CODE]
return v1.cross(v2).normalize(); // not bad
[/CODE]

Free function with return value:

[CODE]return normalize(cross(v1, v2));[/CODE]

I don't know about you guys but I prefer the last option. Of course, as always, [i]it depends[/i]. All those different methods have they strengths and weaknesses, and sometimes there is no clear "best" option. As for performance, unless you are bottlenecking on arithmetic or memory copy (which is [i]highly[/i] unlikely), I wouldn't worry about it. Edited by Bacterius

Share this post


Link to post
Share on other sites
frob    44904
You can always implement both. :-)

I usually prefer class methods but sometimes free functions are more clear. Giving both is not hard and lets the programmer using it decide the most clear version for that particular bit of code. Implement one in terms of the other to save on work, I'd make the class functions first since they don't need temporary values, use them to provide free versions.

Share this post


Link to post
Share on other sites
beebs1    398
Thanks!

[quote name='frob' timestamp='1338183293' post='4943891']
I usually prefer class methods but sometimes free functions are more clear.
[/quote]

Do you prefer the functions (whether class methods or free functions) to modify the original?

For me, I think the following would be confusing:

[CODE]
// modifies the original
Vector3& Vector3::cross(const Vector3&);
Vector3& Vector3::normalise();

// uh oh - v1 and v2 have changed!
return v1.cross(v2).normalise();
[/CODE]

So I guess I'd prefer all methods to return a copy, but maybe free functions are the most unambigous. Just interested in what everyone thinks.

Share this post


Link to post
Share on other sites
SiCrane    11839
I generally expect to be able to tell if a function will modify its arguments from the name of the function. If the function is called normalize() I'd expect it to modify the vector so that it's a unit vector. If the function is called something like normalized_vector() I'd expect it to return a normalized copy. I certainly wouldn't expect a function called cross() to modify either of its arguments.

Share this post


Link to post
Share on other sites
Dawoodoz    461
I want all vector arguments to be taken as values so that I can easily see where the variable's last assignment was made.
[code]
v = normalize(v);
[/code]

I prefer vectors to be plain structs so that different math libraries can be combined without too much work.
When integrating a physics engine with a graphics engine, you get 2 types of vectors that have the same data inside but they don't want to work together.
It would be great if all languages would have a standard datatype for vectors and matrices that everyone can use with their own math functions.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this