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 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();
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..)
};