GLM recommended operator overrides

code-that-i-needed-that-one-time

Find yourself annoyed by the lack of { <, <=, >, >= } operators for glm items? Follow along for thought process and understanding of how to add it.

Since there is no current implementation for those operators, there is no major concern with adding in the implementations.

However, as with any library, always be careful when overriding operators of existing libraries. Especially for the cases where an operator implementation is already in place, it is almost guaranteed to create unintented consequences as the pre-existing operator interactions are probably commonly used and expected throughout the original library.

Just to pick a specific example to work with, we’ll be using vec2 for this explanation.

Including #

Just like with any beginning .h file, we start out by setting up our compiler include guards.

If you already have a pre-existing .h that you’re adding this to, make sure to still note these specified ones around the section of code for glm that we’re about to implement.

We can’t just use #pragma once for this, because we’re uncertain if the compiler has reached this file before the rest of glm (depending on order). That is, we have to define glm’s compiler guards so it knows either to create it when it hits this file or to add to the pre-existing definition.

.h

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

...

#endif

Now we can include the file where the definitions for glm::vec2 are written.

.h

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include <glm/vec2.hpp>

#endif

Note that we use the < > around the include file’s name instead of ".." since these files are outside project includes.

The GLM Namespace #

Now, even though we have this file included, we still don’t have direct editing access to the section that filled out this part of the glm namespace.

Instead, we need to fake our access to this ‘legally’.

To start, we create our own area of the glm namespace without writing using namespace glm;. Calling the using line is a great way to leak the namespace and its scope out to other included files which becomes a major thorn in the side of any C++ compiler. The more cascading namespaces there are, the more likely there will be cases where naming conflicts occur - that is, if you choose the route of using namespace ... you have to confirm that every function call across all namespaces do not share the same name and parameter option combinations. Aka it adds extra overhead that quickly becomes a pain.

Instead, we can create our own section of the namespace. Because it’s within our glm include guards it uses the same namespace definition as that of the included files.

.h

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include <glm/vec2.hpp>
namespace glm {

    ...

} // namespace glm

#endif


Operator Function #

For modifying the <, we need to override that specific operator.

.h

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include <glm/vec2.hpp>
namespace glm {

    return-type operator<(params...) {
        return ...;
    }

} // namespace glm

#endif

Since this is going to be between two glm::vec2s, the operator overload must have them as the two params for this function. The left parameter and right parameter for the left < right comparison.

Note that this works, because we’re implementing the < as a function in a namespace. For cases where this is a method override, that is, an implementation already exists as part of a class and must be overridden there, this workaround is not possible without access to the original file.

We don’t know what kind of glm::vec2 this is (float, int, etc) so we follow glm’s template pattern for the parameters.

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include <glm/vec2.hpp>
namespace glm {

    template <typename T, precision P>
    return-type operator<(  const tvec2<T, P>& left,
                            const tvec2<T, P>& right ) {
        return ...;
    }

} // namespace glm

#endif

most template functions are required to be in .h only by C++ paradigms meaning it will require the inline directive.

(If you’re like me and you’d prefer never to need to use inline and have it properly separated in the .h and .cpp, check out [!! todo - add link to other post !!])

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include <glm/vec2.hpp>
namespace glm {

    template <typename T, precision P>
    inline return-type operator<(   const tvec2<T, P>& left,
                                    const tvec2<T, P>& right) {
        return ...;
    }

} // namespace glm

#endif


< Compare #

Now, we actually implement the < comparison. For our purposes, we want it to be that all values of left must be strictly less than all the values in right.

#ifndef __UTIL_GLM__
#define __UTIL_GLM__

#include "glm/the_vec_hpp_file.hpp"
namespace glm {

    template <typename T, precision P>
    inline bool operator<(  const tvec2<T, P>& left,
                            const tvec2<T, P>& right) {
        return (left.x < right.x && left.y < right.y);
    }

} // namespace glm

#endif