Jump to content

  • Log In with Google      Sign In   
  • Create Account


#ActualAllEightUp

Posted 31 March 2013 - 04:13 PM

Hi folks, continuing on with re-evaluating a bunch of different things in code as preparation to write another article, a fun one came up which actually someone asked a related question about involving statics.  While the question was just "why" you would use it, it applies to the bit I'm considering at this moment.  This may be better suited for the math forum's as it is related to math classes but it really is a general programming topic in terms of good class design.  Anyway, the basic idea is picking on a single function in a 2d or 3d vector class and considering the ups/downs and possible use cases for the function.  While not absolutely critical, I've been re-evaluating it in regards to C++11 (not much change) and common bug scenarios, plenty.  The function is simple: "Normalize".

 

OK, many may be asking just how complicated could "Normalize" be to write.  Well, surprisingly complicated if you want to avoid the most likely set of bug producing use cases.  I normally re-evaluate how I code every couple years in order to break bad habits I've fallen into (hence the question about includes using brackets versus quotes, seems I'm doing OK in reality) and to see if I've learned anything over that time which could be applied and made better.  (Hopefully forming a new habit.)  In this case I was working up the preliminaries to extending on the articles I've been writing by attacking a cross platform vectorized math library using SSE and Neon as targets with a fallback to non-vectorized code as required.  (I.e. SSE is pretty much a given anymore, Neon is most definitely not.)

 

Anyway, let's produce some use cases and questionable results:

 

 

Vector3f  toTarget = target - source;
Vector3f  upVector = Vector3f::kYAxis;
Vector3f  rightVector = toTarget.Normalize() ^ upVector;

 

Make two assumptions from here out please: toTarget is not nearly equal to upVector and unit vector is give or take a certain amount of error around 1.0f.  Otherwise I'll be describing possible edge cases more than getting to the point...  smile.png

 

Ok, so assuming I got the cross product order correct (I ALWAYS forget... smile.png) I should end up with unit vectors of up and right.  Some libraries I've worked with would have a side effect, toTarget would have been normalized in that code.  I despise side effects so any behavior which ends up with toTarget being normalized is out the door, so if I want this code to work as written the signature has to be: "Vector3f Normalize() const".  It could be non-const but that defeats self documentation in saying *this* is not changed after the call, so non const is also out the door.

 

Second use case is a two parter.

 

 

Vector3f incidentVector = -toTarget.Normalize();

 

Again, no side effects allowed so the above change works and we get the negated unit vector as desired.  Now, in "documenting" code I often break up equations, this would not normally be a candidate but assuming this were some big nasty equation with lots going on that needs to be explaining, I may break the code into the following pieces:

 

 

 

Vector3f incidentVector = -toTarget;
incidentVector.Normalize();
 
Do you see the bug?  I have done this sort of thing many times over the years and seen many others do the same thing.  The normalize call no longer "does" anything since it is const and not modifying "this" without an assignment is a nop.  Some compilers will give you warnings in this case and prevent you hunting the bug, but not all notice it.  Not to mention a lot of code bases turn off "unused return value" warnings since there are a pretty numerous set of cases where it is perfectly valid to ignore return values.
 
This leads to two conflicting desires, one is to maintain easy to use code and the other is to prevent silly errors such as case two.
 
So, I was thinking I'd use the following signatures:
 
class Vector3f
{
public:
   void  Normalize();
   friend Vector3f Normalize( const Vector3f& );  // Optional, could just have the helper
  // defined as: { Vector3f result = in; result.Normalize(); return result; }  which most
  // optimizers with do well with.  (NOTE: C++11 I believe the return would be a &&,
  /// still exploring that bit in detail as part of my re-evaluation..)
};
 
Given this you have to modify the first use case to use "Normalize( toVector )" since there is no return to work on from the member function.  The second use case works as intended and the easy to refactor into a bug issue goes away.
 
Now, where all this long winded discussion of a single function ends is: "what am I missing"?  I've fixed a recurring problem and don't see any further problems which the compiler won't catch and error on.  But, I might be too deep in the weeds of one variation to consider the "other" use cases which would lead this to producing yet other common mistakes.  I've tried but can't see any real downfalls other than annoying folks used to the more common signatures.
 
I don't expect a perfect answer, I just don't want to give a poor one if I write an article including this.  smile.png
 
OOPS: Forgot to mention why I was considering the static variation, instead of a helper I was possibly considering using a static "static void Normalize( Vector& )" instead of the void return member.  I.e. "Vector::Normalize( toVector )" as an alternative.  I think the member+helper works better without the extra typing.

#4AllEightUp

Posted 31 March 2013 - 04:10 PM

Hi folks, continuing on with re-evaluating a bunch of different things in code as preparation to write another article, a fun one came up which actually someone asked a related question about involving statics.  While the question was just "why" you would use it, it applies to the bit I'm considering at this moment.  This may be better suited for the math forum's as it is related to math classes but it really is a general programming topic in terms of good class design.  Anyway, the basic idea is picking on a single function in a 2d or 3d vector class and considering the ups/downs and possible use cases for the function.  While not absolutely critical, I've been re-evaluating it in regards to C++11 (not much change) and common bug scenarios, plenty.  The function is simple: "Normalize".

 

OK, many may be asking just how complicated could "Normalize" be to write.  Well, surprisingly complicated if you want to avoid the most likely set of bug producing use cases.  I normally re-evaluate how I code every couple years in order to break bad habits I've fallen into (hence the question about includes using brackets versus quotes, seems I'm doing OK in reality) and to see if I've learned anything over that time which could be applied and made better.  (Hopefully forming a new habit.)  In this case I was working up the preliminaries to extending on the articles I've been writing by attacking a cross platform vectorized math library using SSE and Neon as targets with a fallback to non-vectorized code as required.  (I.e. SSE is pretty much a given anymore, Neon is most definitely not.)

 

Anyway, let's produce some use cases and questionable results:

 

 

Vector3f  toTarget = target - source;
Vector3f  upVector = Vector3f::kYAxis;
Vector3f  rightVector = toTarget.Normalize() ^ upVector;

 

Make two assumptions from here out please: toTarget is not nearly equal to upVector and unit vector is give or take a certain amount of error around 1.0f.  Otherwise I'll be describing possible edge cases more than getting to the point...  smile.png

 

Ok, so assuming I got the cross product order correct (I ALWAYS forget... smile.png) I should end up with unit vectors of up and right.  Some libraries I've worked with would have a side effect, toTarget would have been normalized in that code.  I despise side effects so any behavior which ends up with toTarget being normalized is out the door, so if I want this code to work as written the signature has to be: "Vector3f Normalize() const".  It could be non-const but that defeats self documentation in saying *this* is not changed after the call, so non const is also out the door.

 

Second use case is a two parter.

 

 

Vector3f incidentVector = -toTarget.Normalize();

 

Again, no side effects allowed so the above change works and we get the negated unit vector as desired.  Now, in "documenting" code I often break up equations, this would not normally be a candidate but assuming this were some big nasty equation with lots going on that needs to be explaining, I may break the code into the following pieces:

 

 

 

Vector3f incidentVector = -toTarget;
incidentVector.Normalize();
 
Do you see the bug?  I have done this sort of thing many times over the years and seen many others do the same thing.  The normalize call no longer "does" anything since it is const and not modifying "this" without an assignment is a nop.  Some compilers will give you warnings in this case and prevent you hunting the bug, but not all notice it.  Not to mention a lot of code bases turn off "unused return value" warnings since there are a pretty numerous set of cases where it is perfectly valid to ignore return values.
 
This leads to two conflicting desires, one is to maintain easy to use code and the other is to prevent silly errors such as case two.
 
So, I was thinking I'd use the following signatures:
 
class Vector3f
{
public:
   void  Normalize();
   friend Vector3f Normalize( const Vector3f& );  // Optional, could just have the helper
  // defined as: { Vector3f result = in; result.Normalize(); return result; }  which most
  // optimizers with do well with.  (NOTE: C++11 I believe the return would be a &&,
  /// still exploring that bit in detail as part of my re-evaluation..)
};
 
Given this you have to modify the first use case to use "Normalize( toVector )" since there is no return to work on from the member function.  The second use case works as intended and the easy to refactor into a bug issue goes away.
 
Now, where all this long winded discussion of a single function ends is: "what am I missing"?  I've fixed a recurring problem and don't see any further problems which the compiler won't catch and error on.  But, I might be too deep in the weeds of one variation to consider the "other" use cases which would lead this to producing yet other common mistakes.  I've tried but can't see any real downfalls other than annoying folks used to the more common signatures.
 
I don't expect a perfect answer, I just don't want to give a poor one if I write an article including this.  smile.png
 
OOPS: Forgot to mention why I was considering the static variation, instead of a helper I was possibly considering using a static "static void Normalize( Vector& )" instead of the void return member.  I.e. "Vector::Normalize( toVector )" as an alternative.  I think the helper works better without the extra typing.

#3AllEightUp

Posted 31 March 2013 - 04:06 PM

Hi folks, continuing on with re-evaluating a bunch of different things in code as preparation to write another article, a fun one came up which actually someone asked a related question about involving statics.  While the question was just "why" you would use it, it applies to the bit I'm considering at this moment.  This may be better suited for the math forum's as it is related to math classes but it really is a general programming topic in terms of good class design.  Anyway, the basic idea is picking on a single function in a 2d or 3d vector class and considering the ups/downs and possible use cases for the function.  While not absolutely critical, I've been re-evaluating it in regards to C++11 (not much change) and common bug scenarios, plenty.  The function is simple: "Normalize".

 

OK, many may be asking just how complicated could "Normalize" be to write.  Well, surprisingly complicated if you want to avoid the most likely set of bug producing use cases.  I normally re-evaluate how I code every couple years in order to break bad habits I've fallen into (hence the question about includes using brackets versus quotes, seems I'm doing OK in reality) and to see if I've learned anything over that time which could be applied and made better.  (Hopefully forming a new habit.)  In this case I was working up the preliminaries to extending on the articles I've been writing by attacking a cross platform vectorized math library using SSE and Neon as targets with a fallback to non-vectorized code as required.  (I.e. SSE is pretty much a given anymore, Neon is most definitely not.)

 

Anyway, let's produce some use cases and questionable results:

 

 

Vector3f  toTarget = target - source;
Vector3f  upVector = Vector3f::kYAxis;
Vector3f  rightVector = toTarget.Normalize() ^ upVector;

 

Make two assumptions from here out please: toTarget is not nearly equal to upVector and unit vector is give or take a certain amount of error around 1.0f.  Otherwise I'll be describing possible edge cases more than getting to the point...  smile.png

 

Ok, so assuming I got the cross product order correct (I ALWAYS forget... smile.png) I should end up with unit vectors of up and right.  Some libraries I've worked with would have a side effect, toTarget would have been normalized in that code.  I despise side effects so any behavior which ends up with toTarget being normalized is out the door, so if I want this code to work as written the signature has to be: "Vector3f Normalize() const".  It could be non-const but that defeats self documentation in saying *this* is not changed after the call, so non const is also out the door.

 

Second use case is a two parter.

 

 

Vector3f incidentVector = -toTarget.Normalize();

 

Again, no side effects allows so the above change works and we get the negated unit vector as desired.  Now, in "documenting" code I often break up equations, this would not normally be a candidate but assuming this were some big nasty equation with lots going on that needs to be explaining, I may break the code into the following pieces:

 

 

 

Vector3f incidentVector = -toTarget;
incidentVector.Normalize();
 
Do you see the bug?  I have done this sort of thing many times over the years and seen many others do the same thing.  The normalize call no longer "does" anything since it is const and not modifying "this" without an assignment is a nop.  Some compilers will give you warnings in this case and prevent you hunting the bug, but not all notice it.  Not to mention a lot of code bases turn off "unused return value" warnings since there are a pretty numerous set of cases where it is perfectly valid to ignore return values.
 
This leads to two conflicting desires, one is to maintain easy to use code and the other is to prevent silly errors such as case two.
 
So, I was thinking I'd use the following signatures:
 
class Vector3f
{
public:
   void  Normalize();
   friend Vector3f Normalize( const Vector3f& );  // Optional, could just have the helper
  // defined as: { Vector3f result = in; result.Normalize(); return result; }  which most
  // optimizers with do well with.  (NOTE: C++11 I believe the return would be a &&,
  /// still exploring that bit in detail as part of my re-evaluation..)
};
 
Given this you have to modify the first use case to use "Normalize( toVector )" since there is no return to work on from the member function.  The second use case works as intended and the easy to refactor into a bug issue goes away.
 
Now, where all this long winded discussion of a single function ends is: "what am I missing"?  I've fixed a recurring problem and don't see any further problems which the compiler won't catch and error on.  But, I might be too deep in the weeds of one variation to consider the "other" use cases which would lead this to producing yet other common mistakes.  I've tried but can't see any really downfalls other than annoying folks used to the more common signatures.
 
I don't expect a perfect answer, I just don't want to give a poor one if I write an article including this.  smile.png
 
OOPS: Forgot to mention why I was considering the static variation, instead of a helper I was possibly considering using a static "static void Normalize( Vector& )" instead of the void return member.  I.e. "Vector::Normalize( toVector )" as an alternative.  I think the helper works better without the extra typing.

#2AllEightUp

Posted 31 March 2013 - 03:50 PM

Hi folks, continuing on with re-evaluating a bunch of different things in code as preparation to write another article, a fun one came up which actually someone asked a related question about involving statics.  While the question was just "why" you would use it, it applies to the bit I'm considering at this moment.  This may be better suited for the math forum's as it is related to math classes but it really is a general programming topic in terms of good class design.  Anyway, the basic idea is picking on a single function in a 2d or 3d vector class and considering the ups/downs and possible use cases for the function.  While not absolutely critical, I've been re-evaluating it in regards to C++11 (not much change) and common bug scenarios, plenty.  The function is simple: "Normalize".

 

OK, many may be asking just how complicated could "Normalize" be to write.  Well, surprisingly complicated if you want to avoid the most likely set of bug producing use cases.  I normally re-evaluate how I code every couple years in order to break bad habits I've fallen into (hence the question about includes using brackets versus quotes, seems I'm doing OK in reality) and to see if I've learned anything over that time which could be applied and made better.  (Hopefully forming a new habit.)  In this case I was working up the preliminaries to extending on the articles I've been writing by attacking a cross platform vectorized math library using SSE and Neon as targets with a fallback to non-vectorized code as required.  (I.e. SSE is pretty much a given anymore, Neon is most definitely not.)

 

Anyway, let's produce some use cases and questionable results:

 

 

Vector3f  toTarget = target - source;
Vector3f  upVector = Vector3f::kYAxis;
Vector3f  rightVector = toTarget.Normalize() ^ upVector;

 

Make two assumptions from here out please: toTarget is not nearly equal to upVector and unit vector is give or take a certain amount of error around 1.0f.  Otherwise I'll be describing possible edge cases more than getting to the point...  smile.png

 

Ok, so assuming I got the cross product order correct (I ALWAYS forget... smile.png) I should end up with unit vectors of up and right.  Some libraries I've worked with would have a side effect, toTarget would have been normalized in that code.  I despise side effects so any behavior which ends up with toTarget being normalized is out the door, so if I want this code to work as written the signature has to be: "Vector3f Normalize() const".  It could be non-const but that defeats self documentation in saying *this* is not changed after the call, so non const is also out the door.

 

Second use case is a two parter.

 

 

Vector3f incidentVector = -toTarget.Normalize();

 

Again, no side effects allows so the above change works and we get the negated unit vector as desired.  Now, in "documenting" code I often break up equations, this would not normally be a candidate but assuming this were some big nasty equation with lots going on that needs to be explaining, I may break the code into the following pieces:

 

 

 

Vector3f incidentVector = -toTarget;
incidentVector.Normalize();
 
Do you see the bug?  I have done this sort of thing many times over the years and seen many others do the same thing.  The normalize call no longer "does" anything since it is const and not modifying "this" without an assignment is a nop.  Some compilers will give you warnings in this case and prevent you hunting the bug, but not all notice it.  Not to mention a lot of code bases turn off "unused return value" warnings since there are a pretty numerous set of cases where it is perfectly valid to ignore return values.
 
This leads to two conflicting desires, one is to maintain easy to use code and the other is to prevent silly errors such as case two.
 
So, I was thinking I'd use the following signatures:
 
class Vector3f
{
public:
   void  Normalize();
   friend Vector3f Normalize( const Vector3f& );  // Optional, could just have the helper
  // defined as: { Vector3f result = in; result.Normalize(); return result; }  which most
  // optimizers with do well with.  (NOTE: C++11 I believe the return would be a &&,
  /// still exploring that bit in detail as part of my re-evaluation..)
};
 
Given this you have to modify the first use case to use "Normalize( toVector )" since there is no return to work on from the member function.  The second use case works as intended and the easy to refactor into a bug issue goes away.
 
Now, where all this long winded discussion of a single function ends is: "what am I missing"?  I've fixed a recurring problem and don't see any further problems which the compiler won't catch and error on.  But, I might be too deep in the weeds of one variation to consider the "other" use cases which would lead this to producing yet other common mistakes.  I've tried but can't see any really downfalls other than annoying folks used to the more common signatures.
 
I don't expect a perfect answer, I just don't want to give a poor one if I write an article including this.  smile.png

#1AllEightUp

Posted 31 March 2013 - 03:46 PM

Hi folks, continuing on with re-evaluating a bunch of different things in code as preparation to write another article, a fun one came up which actually someone asked a related question about involving statics.  While the question was just "why" you would use it, it applies to the bit I'm considering at this moment.  This may be better suited for the math forum's as it is related to math classes but it really is a general programming topic in terms of good class design.  Anyway, the basic idea is picking on a single function in a 2d or 3d vector class and considering the ups/downs and possible use cases for the function.  While not absolutely critical, I've been re-evaluating it in regards to C++11 (not much change) and common bug scenarios, plenty.  The function is simple: "Normalize".

 

OK, many may be asking just how complicated could "Normalize" be to write.  Well, surprisingly complicated if you want to avoid the most likely set of bug producing use cases.  I normally re-evaluate how I code every couple years in order to break bad habits I've fallen into (hence the question about includes using brackets versus quotes, seems I'm doing OK in reality) and to see if I've learned anything over that time which could be applied and made better.  (Hopefully forming a new habit.)  In this case I was working up the preliminaries to extending on the articles I've been writing by attacking a cross platform vectorized math library using SSE and Neon as targets with a fallback to non-vectorized code as required.  (I.e. SSE is pretty much a given anymore, Neon is most definitely not.)

 

Anyway, let's produce some use cases and questionable results:

 

Vector3f  toTarget = target - source;
Vector3f  upVector = Vector3f::kYAxis;
Vector3f  rightVector = toTarget.Normalize() ^ upVector;

 

Make two assumptions from here out please: toTarget is not nearly equal to upVector and unit vector is give or take a certain amount of error around 1.0f.  Otherwise I'll be describing possible edge cases more than getting to the point...  :)

 

Ok, so assuming I got the cross product order correct (I ALWAYS forget... :)) I should end up with unit vectors of up and right.  Some libraries I've worked with would have a side effect, toTarget would have been normalized in that code.  I despise side effects so any behavior which ends up with toTarget being normalized is out the door, so if I want this code to work as written the signature has to be: "Vector3f Normalize() const".  It could be non-const but that defeats self documentation in saying *this* is not changed after the call, so non const is also out the door.

 

Second use case is a two parter.

 

Vector3f incidentVector = -toTarget.Normalize();

 

Again, no side effects allows so the above change works and we get the negated unit vector as desired.  Now, in "documenting" code I often break up equations, this would not normally be a candidate but assuming this were some big nasty equation with lots going on that needs to be explaining, I may break the code into the following pieces:

 

 

Vector3f incidentVector = toTarget;
incidentVector.Normalize();
 
Do you see the bug?  I have done this sort of thing many times over the years and seen many others do the same thing.  The normalize call no longer "does" anything since it is const and not modifying "this" without an assignment is a nop.  Some compilers will give you warnings in this case and prevent you hunting the bug, but not all notice it.  Not to mention a lot of code bases turn off "unused return value" warnings since there are a pretty numerous set of cases where it is perfectly valid to ignore return values.
 
This leads to two conflicting desires, one is to maintain easy to use code and the other is to prevent silly errors such as case two.
 
So, I was thinking I'd use the following signatures:
 
class Vector3f
{
public:
   void  Normalize();
   friend Vector3f Normalize( const Vector3f& );  // Optional, could just have the helper
  // defined as: { Vector3f result = in; result.Normalize(); return result; }  which most
  // optimizers with do well with.  (NOTE: C++11 I believe the return would be a &&,
  /// still exploring that bit in detail as part of my re-evaluation..)
};
 
Given this you have to modify the first use case to use "Normalize( toVector )" since there is no return to work on from the member function.  The second use case works as intended and the easy to refactor into a bug issue goes away.
 
Now, where all this long winded discussion of a single function ends is: "what am I missing"?  I've fixed a recurring problem and don't see any further problems which the compiler won't catch and error on.  But, I might be too deep in the weeds of one variation to consider the "other" use cases which would lead this to producing yet other common mistakes.  I've tried but can't see any really downfalls other than annoying folks used to the more common signatures.
 
I don't expect a perfect answer, I just don't want to give a poor one if I write an article including this.  :)

PARTNERS