Boost QVM

Interoperability

An important design goal of Boost QVM is that it works seamlessly with 3rd-party quaternion, matrix and vector types and libraries. Even when such libraries overload the same C++ operators as Boost QVM, it is usually safe to bring the entire boost::qvm namespace in scope by specifying:

using namespace boost::qvm;

Usually, the above using directive does not introduce ambiguities with function and operator overloads a 3rd-party library may define because:

  • Most boost::qvm function overloads and all operator overloads use SFINAE, which makes them disappear unless an expression uses types that have the appropriate Boost QVM-specific type traits defined;
  • Whenever such overloads are compatible with a given expression, their signature is extremely generic, which means that any other compatible overload will be a better match in any overload resolution.

Bringing the boost::qvm namespace in scope lets you mix vector and matrix types that come from different APIs into a common, type-safe framework. In this case however, it should be considered what types should be returned by binary operations that return an object by value. For example, if you multiply a 3x3 matrix m1 of type user_matrix1 by a 3x3 matrix m2 of type user_matrix2, what type should that operation return?

The answer is that by default, Boost QVM returns some kind of compatible matrix type, so (in C++0x terms) it is always safe to write:

auto m = m1 * m2;

However, the type deduced by default converts implicitly to any compatible matrix type, so the following is also valid, at the cost of a temporary:

user_matrix1 m = m1 * m2;

While the temporary object can be optimized away by many compilers, it can be avoided altogether by specializing the deduce_m2 template. For example, to specify that multiplying a user_matrix1 by a user_matrix2 should always produce a user_matrix1 object, you could specify:

namespace
boost
    {
    namespace
    qvm
        {
        template <>
        struct deduce_m2<user_matrix1,user_matrix2,3,3>
            { typedef user_matrix1 type; };

        template <>
        struct deduce_m2<user_matrix2,user_matrix1,3,3>
            { typedef user_matrix1 type; };
        }
    }

Finally, any time you need to create a matrix of a particular C++ type from any other compatible matrix type, you can use the make function:

user_matrix2 m=make<user_matrix2>(m1 * m2);

Perhaps surprisingly, unary operations that return an object by value have a similar, though simpler issue. That's because the argument they're called with may not be copyable, as in:

float m[3][3];
auto inv = inverse(m);

Again, Boost QVM "just works", returning an object of suitable matrix type that is copyable. This deduction process can also be controlled, by specializing the deduce_m template.

Note: Bringing the entire boost::qvm namespace in scope may introduce ambiguities when accessing types defined in 3rd-party libraries. In that case, you can safely bring namespace boost::qvm::sfinae in scope instead, which contains only function and operator overloads that use SFINAE.


Tutorial: Quaternion, Vector and Matrix Types | C Arrays | Views | Swizzling | Interoperability | Back to Boost QVM