Create a template meta-function to create an identity matrix at compile time
I was asking myself if it is possible to create a matrix unit at compile time if the size is known. So far I've just written an example of creating a matrix based on a std :: vector with a fixed size, eg. 4x4. However, I'm not sure how to set the values. I think I need recursion: /
// Example program
#include <iostream>
#include <string>
#include <vector>
template <class T>
using vec1D = std::vector<T>;
template <class T>
using vec2D = std::vector<std::vector<T>>;
template <class T, int size>
vec2D<T> make_mat() {
vec2D<T> mat(size, vec1D<T>(size));
return mat;
}
int main()
{
vec2D<float> unit = make_mat<float, 4>();
std::cout
<< unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
<< unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
<< unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
<< unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}
source to share
I was asking myself if it is possible to create a matrix unit at compile time if the size is known.
I don't think if your matrix std::vector
.
But if it's based on std::array
and you want it to be zero-initialized (as in your example) yes: it's possible.
// Example program
#include <iostream>
#include <array>
template <typename T, std::size_t D1, std::size_t D2 = D1>
constexpr std::array<std::array<T, D2>, D1> doA ()
{ return { }; }
int main()
{
constexpr auto unit = doA<float, 4U>();
std::cout
<< unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
<< unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
<< unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
<< unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}
But I don't think that's a good idea: assume you're std::array
using the stack, not the heap. So this solution is only useful for small (very small) matrices.
--- EDIT ---
OP (correctly) observes
the question was if init can initialize the matrix as the identity matrix: 100 010 001
Yes, it is possible with the identity matrix; a little more complicated, but possible.
// Example program
#include <iostream>
#include <utility>
#include <array>
template <typename T, std::size_t I, std::size_t ... Is>
constexpr auto doU_helper2 (std::index_sequence<Is...> const &)
{ return std::array<T, sizeof...(Is)>
{ { (Is == I ? T{1} : T{0})... } }; }
template <typename T, std::size_t ... Is>
constexpr auto doU_helper1 (std::index_sequence<Is...> const & is)
{ return std::array<std::array<T, sizeof...(Is)>, sizeof...(Is)>
{ { doU_helper2<T, Is>(is)... } }; }
template <typename T, std::size_t Dim>
constexpr auto doU ()
{ return doU_helper1<T>(std::make_index_sequence<Dim>{}); }
int main()
{
constexpr auto unit = doU<float, 4U>();
std::cout
<< unit[0][0] << unit[0][1] << unit[0][2] << unit[0][3] << std::endl
<< unit[1][0] << unit[1][1] << unit[1][2] << unit[1][3] << std::endl
<< unit[2][0] << unit[2][1] << unit[2][2] << unit[2][3] << std::endl
<< unit[3][0] << unit[3][1] << unit[3][2] << unit[3][3] << std::endl;
}
Consider this code uses std::make_index_sequence{}
and std::index_sequence
, which are C ++ 14 features.
If you need a C ++ 11 solution, it's not too hard to create a subheading; ask if you need it (I did somewhere, I only need to get it).
source to share
You cannot do dynamic allocation at compile time.
You can do this at runtime. The easy way is with loops.
If you want to avoid loops entirely:
template<std::size_t...Is>
auto index_over(std::index_sequence<Is...>){
return [](auto&&f)->decltype(auto){
return decltype(f)(f)(std::integral_constant<std::size_t,Is>{}...);
};
}
template<std::size_t N>
auto index_upto(std::integral_constant<std::size_t,N> ={}){
return index_over(std::make_index_sequence<N>{});
}
template <class T>
using vec1D = std::vector<T>;
template <class T>
using vec2D = std::vector<std::vector<T>>;
template<std::size_t N, std::size_t I,class T>
vec1D<T> make_row( T const& base, T const& diag ){
return index_upto<I>()([&](auto...Before){
return index_upto<N-I-1>()([&](auto...After)->vec1D<T>{
return {
(void(Before), base)...,
diag,
(void(After), base)...
};
});
});
}
template<std::size_t N, class T>
vec2D<T> make_mat( T const& base, T const& diag){
return index_upto<N>()([&}(auto...Rs)->vec2D<T>{
return {
make_row<N, Rs>(base, diag)...
};
});
}
may contain typos (written by phone, not verified). Uses constructs that are legal C ++, but g ++ sometimes chokes; Klang will understand this. C ++ 14.
The before and after index avoids having to write a bunch of helper functions to extend the index parameter packages; they allow expansion to lambda.
If your compiler complains about Rs
which is being used at compile time, replace with decltype(Rs)::value
- your compiler has failed to evaluate constexpr
.
Doing this in C ++ 11 is annoying. I could use helper types to extend the package, one for each function I wrote above. You also want to rewrite the index sequence from C ++ 14. It's very meh, just a loop.
auto u = make_mat<4>( 0, 1 );
creates a int
4x4 diagonal matrix .
Note that this method, with liberal constexpr
size and type, works with std array. Unfortunately, lambda couldn't be constexpr until C ++ 17.
The loop solution also works with constexpr
post C ++ 14 matrices; you can mutate local data in constexpr.
source to share