I basically try to write my own game engine for practice and personal use (I know, it's a nearly impossible task, but as I said, it's mostly for learning new things).
Currently, I'm working on my math library (mainly vectors and matrices) and I came across an interesting, but mostly, aesthetic problem.
The following pseudo-code is given:
template <uint8 size>
struct TVector {
float elements[size];
};
Now I want to be able to construct the struct with the required amount of floats as parameters:
TVector<3> vec0(1.0f, 2.5f, -4.0f);
TVector<2> vec1(3.0f, -2.0f);
TVector<3> vec2(2.0f, 2.2f); // Error: arg missing
TVector<2> vec3(1.0f, 2.0f, 3.0f) // Error: too many args
Since the size of array is given by the template parameter, I struggled with declaring a fitting constructor for the struct. My ultimate goal would be something like this:
// This is pseudo-ideal-code
TVector(size * (float value)); // Create a constructor with number of size
// parameters, which are all floats
Of course, this is non-logical syntax, but the closest thing I achieved in that manner was with C++17 fold expressions:
template<typename... Args>
TVector(Args... values) {
static_assert(sizeof...(values) <= size, "Too many args");
uint8 i = 0;
(... , void(elements[i++] = values));
}
It works perfectly fine in sense of filling the array and (I guess) is not much overhead, but it is also error-prone for the programmer who uses this struct, since it gives no direct indication of how many arguments the constructor takes in.
Furthermore, it does not specify which type of the arguments should be and this is my biggest problem here.
Why is it a problem if it works?
Imagine having the following struct, which uses the TVector struct:
template <const uint8 rows, const uint8 columns>
struct TMatrix {
// elements[-columns-][-rows-];
TVector<rows> elements[columns];
}
Given, that the constructor is similar to the fold expression of the vector struct, I want to be able to construct the matrix with the accordingly sized vectors or brace initialization.
The aggregate initialization does not work.
TVector<2> vec(1.0f, 3.0f); TMatrix<2, 2> mat0(vec, vec); // Works TMatrix<2, 2> mat1(vec, {0.2f, -4.2f}); // Error // Does not compile, because the Type is not clear
It does not show an error until compilation when given the wrong parameters (like a vector with a wrong size, that would not fit as column of the matrix).
The source of the error is not always clear.
TL;DR: Now finally to my real question here:
Is there a way to limit the type of a fold expression, ultimately not using templates at all and solving my 3 problems given above?
I imagine something like:
TVector(float... values) {
// Maybe even specify the size of the pack with the size given in the struct template
uint8 i = 0;
(... , void(elements[i++] = values));
}
And:
TMatrix(const TVector<rows>&... values) {
uint8 i = 0;
(..., void(elements[i++] = values));
}
Of course, I am being very picky here and this is mostly an aesthetic problem, but I think it is an important design decision, which could really improve code usability.
Thank you for reading this and helping me with my first question here :)