Lowecase didn't prove to be better, because Doxygen cannot implicitly
link to it and it collides with non-type template parameters and private
variables.
Loop unrolling is better to leave up to the compiler, as it will do it
automatically and it doesn't add any maintenance burden. Constexpr
addition, multiplication etc. of Vector would be nice, but will that be
really useful? Maybe once if at all?
Now all possible cases are properly handled (row vector * column vector,
column vector * row vector, ...). All operators taking arbitrary type as
argument (element-wise multiplication) now have std::enable_if only for
numerical types.
Currently moved only non-square functionality from Matrix there. Also
static constant members such as row/column count and size are now
lowercase, as they are variables, not types.
NumericType is corresponding numeric type with size at least the same as
int. It is used in debug operators for Matrix and Vector to prevent
printing chars as characters.
If the type isn't already floating-point, FloatingPointType is
corresponding larger type with sufficient size for normalization of
given integral type.
Also updated type traits for long types, they are now subclassed either
from int or long long based on sizeof(long).
On the other hand everything of this is done at runtime, so it's less
performant than the previous version, mainly when used in loops. When
the result is declared as constexpr, it is done at compile time, just
like the previous version.
I don't know which version to keep, so there will be both until a good
decision.
Doxygen produces some false-positive warnings for Matrix and Vector
classes, but the generated documentation is fine. Worked around the
warnings by using @copybrief and @copydetails instead of @copydoc.
C++ allows creating arrays with initializer lists shorter than array
length, but for vectors and matrices it will be error prone and hard to
debug. Removed deleted constructor, as it is now catched with
static_assert as well. Also this was possible before (and wasn't catched
with the deleted constructor), now isn't:
Matrix<2, int> a(1, 2);
Each function which returned e.g. Vector<size, T> was in subclasses
overloaded with function returning e.g. Vector3<T>, so the user is able
to use subclass-specific functions. It was nightmare to maintain and it
cluttered the documentation a lot.
Long-standing TODO. It is better to have size first, because it is more
significant than type (e.g. because there are Vector4<T> specializations
and not VectorT<4> specializations). It is also IMHO easier for user to
distinguish/read the type than before:
Vector<float, 4> -> Vector4<float> // before
Vector<4, float> -> Vector4<float> // now
GCC does some heavy magic optimizations in -O2 (-O1 works) and it
somewhat breaks them. It should be safe to use them outside of Matrix,
though (e.g. when not used in loops through all elements).
Before it was one constructor using bool parameter, which is massive
antipattern:
Matrix4 m(false); // Huh? No Matrix4 then or what?
Iẗ́'s now separated into two distinct constructors, of them one can be
already declared as constexpr (hooray). The usage is as follows:
Matrix4 a; // Default (identity matrix)
Matrix4 b(Matrix4::Identity); // Explicitly identity matrix
Matrix4 c(Matrix4::Zero); // Zero-filled matrix
Also deleted constructor from one parameter, so following mistakes
now cannot compile:
Matrix4 d(true); // Both would set element at [0][0] to 1,
Matrix4 e(Matrix3::Zero); // and other to 0
Removed functions at(), set() and add(), everything (and more) can be
now done using operator[]. Accessing matrix elements is now done through
column vectors, e.g.:
Matrix4 a;
a.at(row, col); // before
a[col][row]; // now
Note that because operator[] on Matrix returns column vector (there is
nothing like row vector), the parameter "order" is now swapped.
Types saved inside Matrix and Vector will be at most time smaller than
or the same size as references to them, so no move semantic and
forwarding is necessary.
It should improve performance, because what can be computed at compile
time is now computed at compile time. In addition these things have been
changed or improved:
* Removed constructors from T*, as they cannot be AFAIK written using
constexpr functions only and they only do unnecessary data copying
most of the time. The functionality is now provided using static
functions Matrix*::from() and Vector*::from() which returns either
const or non-const reference to original data, so no copy is
performed. These functions also have explicit warning about unsafe
operations.
* Defaulted copy constructors and assignment operators, using
"default initialization" for arrays instead of memsetting it with
zeros. It should behave the same and this way we don't need any
memcpy(), memset() etc. from <cstring>, which is good.