Jump to content

  • Log In with Google      Sign In   
  • Create Account


Which do you prefer?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 Telios   Members   -  Reputation: 398

Like
0Likes
Like

Posted 27 May 2012 - 08:37 AM

Hiya,

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

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

	// Or this?
	Vector3& normalise();

	// Or this?
	Vector3 normalise() const;
}

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.

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

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

Edited by Telios, 27 May 2012 - 09:39 AM.


Sponsor:

#2 TheUnnamable   Members   -  Reputation: 779

Like
2Likes
Like

Posted 27 May 2012 - 09:40 AM

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:
class Vector3;
Vector3 Normalise(const Vector3&);

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:
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();
Ta-dah!

#3 Álvaro   Crossbones+   -  Reputation: 12580

Like
3Likes
Like

Posted 27 May 2012 - 09:48 AM

I actually think a free function is more clear:

Vector3 normalize(Vector3 const &v);

Vector3 v1;
Vector3 v2 = normalize(v1);


#4 Muzzy A   Members   -  Reputation: 624

Like
1Likes
Like

Posted 27 May 2012 - 10:32 AM

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, 27 May 2012 - 10:33 AM.


#5 Telios   Members   -  Reputation: 398

Like
1Likes
Like

Posted 27 May 2012 - 11:31 AM

Thanks for that! I think I'm leaning towards free functions too, but then I'd feel I need to make everything 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:

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

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

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:

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

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

Although such copies are unlikely to be a bottleneck.

...free function with the Correct spelling lol.


I'm not even gonna go there Posted Image

Edited by Telios, 27 May 2012 - 11:33 AM.


#6 Bacterius   Crossbones+   -  Reputation: 8363

Like
1Likes
Like

Posted 27 May 2012 - 08:17 PM

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:

square(x);
multiply(x, 3);
add(x, 2);
return x;

Class function:

x.square();
x.multiply(3);
x.add(2);
return x;

Or, if the class function returns itself:

return x.square().multiply(3).add(2);

Free function with return value:

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

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:

cross(v1, v2); // modifies v1
normalize(v1);
return v1;

Class function:

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

Free function with return value:

return normalize(cross(v1, v2));

I don't know about you guys but I prefer the last option. Of course, as always, it depends. 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 highly unlikely), I wouldn't worry about it.

Edited by Bacterius, 27 May 2012 - 08:21 PM.

The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#7 frob   Moderators   -  Reputation: 19867

Like
0Likes
Like

Posted 27 May 2012 - 11:34 PM

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.
Check out my personal indie blog at bryanwagstaff.com.

#8 Telios   Members   -  Reputation: 398

Like
0Likes
Like

Posted 28 May 2012 - 11:53 AM

Thanks!

I usually prefer class methods but sometimes free functions are more clear.


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

For me, I think the following would be confusing:

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

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

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.

#9 SiCrane   Moderators   -  Reputation: 9502

Like
2Likes
Like

Posted 28 May 2012 - 01:41 PM

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.

#10 Dawoodoz   Members   -  Reputation: 294

Like
0Likes
Like

Posted 29 May 2012 - 12:02 PM

I want all vector arguments to be taken as values so that I can easily see where the variable's last assignment was made.
v = normalize(v);

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.

My open source DirectX 10/11 graphics engine. https://sites.google.com/site/dawoodoz

"My design pattern is the simplest to understand. Everyone else is just too stupid to understand it."





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS