Compile time template values deduction - c++

I have this template matrix struct (I provided a constructor which takes std::initializer_list):
template<int rows, int cols, typename scalar = float>
struct matrix;
with a product operator defined outside the matrix struct, like this:
template<int n, int m, int p, typename scalar>
matrix<n, m, scalar> operator*(const matrix<m, p, scalar>& left, const matrix<p, n, scalar>& left);
and then declared as a friend inside the struct. So if I instantiate two matrices:
matrix<2, 3> A = { 1, 2, 3, 4, 5, 6 };
matrix<3, 2> B = { 7, 8, 9, 10, 11, 12 };
and I want to create a matrix C = A * B, I have to write:
matrix<2, 2> C = A * B;
And that's fine, but is there a way to omit the <2, 2> template? I believe that it can be deducted at compile time (because auto works fine):
auto C = A * B; // no errors
I'd like to write just matrix instead of auto, is it possible?

No, you cannot (if you don't have some non-template base matrix). matrix is not a type, it's template and you should specify template parameters. auto is simplest thing, that you can do. Or, instead of auto you can use decltype
decltype(A * B) C = A * B;

Related

How to define a type dependent on template parameters

I have a class that has methods that will return Eigen::Matrix types. However, when these will be 1x1 matrices, I want to just return double, as treating scalars as Eigen::Matrix<double, 1, 1> is a bit nasty. Essentially, I want something like this:
template <int rows, int cols>
class foo
{
using my_type = ((rows == 1 && cols == 1) ? double : Eigen::Matrix<double, rows, cols>);
};
However, this (unsurprisingly) doesn't work. How can I create the equivalent of the above?
Use std::conditional_t<cond, TThen, TElse>:
template <int rows, int cols>
class foo
{
using my_type = std::conditional_t<rows == 1 && cols == 1, double, Eigen::Matrix<double, rows, cols>>;
};
If you don't yet have std::conditional_t<>, you might try std::conditional<>::type (with typename prefix).

Is there any way to set all elements of a matrix to 1 in C++?

Say I am declaring a matrix like this--> long long int A[100][100]. Is there any way to assign the value 1 to all its elements at the time of declaring the matrix?
You can use the fill_n method and initialize array like this:
std::fill_n(&A[0][0], sizeof(A) / sizeof(**A), 1);
Check this for more information about fill_na.
Hope it solves your question.
Is there any way to assign the value 1 to all its elements at the time of declaring the matrix?
I interpret that as you want something constexpr and I'd use std::array instead of plain arrays since std::arrays are easier to work with.
I've made templates for make_array that creates a 1 dimensional array and templates for make_2d_array that creates a 2 dimensional array, using the first set of templates.
The goal of the templates is that if we for example want a 2d array with 2 rows and 3 columns of int with all ints initialized with 1, this should be created:
std::array<std::array<int, 3>, 2> A{{
{1, 1, 1},
{1, 1, 1}
}};
In the code below make_array<int, 3, 1>() will create the inner arrays, std::array<int, 3>{1, 1, 1}, and make_2d_array<int, 2, 3, 1>() will be equivalent to:
std::array<std::array<int, 3>, 2> A{
make_array<int, 3, 1>(), // std::array<int, 3>{1, 1, 1}
make_array<int, 3, 1>() // std::array<int, 3>{1, 1, 1}
};
These are a set of templates that can accomplish that:
#include <array>
#include <type_traits>
#include <utility>
//-------------------------------------------------------------
template <typename T, T init, std::size_t... X>
constexpr std::array<T, sizeof...(X)>
make_array(std::index_sequence<X...>) {
//
// used for folding only then discarded below
// |
return std::array<T, sizeof...(X)>{ ((void)(X), (init))... };
}
template <typename T, std::size_t X, T init = T{}>
constexpr std::array<T, X> make_array() {
return make_array<T, init>(std::make_index_sequence<X>{});
}
//-------------------------------------------------------------
template <typename T, T init, std::size_t X, std::size_t... Y>
constexpr std::array< std::array<T, X>, sizeof...(Y)>
make_2d_array(std::index_sequence<Y...>) {
return std::array< std::array<T, X>, sizeof...(Y)>{
//
// Y used for folding only and discarded
// |
((void)(Y), make_array<T, X, init>())...
};
}
template <typename T, std::size_t Y, std::size_t X, T init = T{}>
constexpr std::array<std::array<T, X>, Y>
make_2d_array() {
return make_2d_array<T, init, X>(std::make_index_sequence<Y>{});
}
It can then be used like this:
constexpr auto A = make_2d_array<int, 2, 3, 1>(); // Y=2, X=3, init value 1
Full demo
(click on the Cppinsights link top left in the demo if you'd like to see how the folding unfolds)
As shown in Ted Lyngmo's answer, instead of a long long int A[100][100] we can declare and initialize a std::array<std::array<long long, 100>, 100> using a constexpr function.
Since C++20, it's possible to declare a function with a consteval specifier which
specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant.
Moreover, the algorithm std::fill_n has been made constexpr.
So that we can write the following two function templates:
#include <algorithm>
#include <array>
template <class Type, std::size_t Size>
consteval auto make_1d_array(Type value)
{
std::array<Type, Size> result;
std::fill_n(result.begin(), Size, value);
return result;
}
template <class Type, std::size_t Rows, std::size_t Cols>
consteval auto make_2d_array(Type value)
{
std::array<std::array<Type, Cols>, Rows> result;
std::fill_n( result.begin(), Rows
, make_1d_array<Type, Cols>(value) );
return result;
}
Live demo.

Disambiguate recursive definition of template function

I am building a statically typed Matrix where all operations with matrices are typechecked. However, I am having issues when I want to do something that modifies the Matrix based on given number.
For instance, adding one column is trivial:
template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
return Matrix<A,B+1>();
}
However, adding N columns is way harder. Since is impossible to typecheck with a function that has a branch where the return type is not the expected (even if the branch condition guarantees it) I can only think about a recursive approach:
template<int A, int B, int Z>
Matrix<A,B+1> addZCols(Matrix<A,B> m1) {
return addOneCol(m1);
}
template<int A, int B, int Z>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
return addOneCol(addZCols<A,B,Z-1>(m1));
}
template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
return Matrix<A,B+1>();
}
However, this is overloading addZCols in the return type, what is not allowed and leads to an error saying that calling addZCalls is ambiguous and cannot chose one of the 2 candidates. And what I want is that the version wiht B+1 is only called as the base case, so to speak, when Z=1.
Any idea about how to make this work or a different approach?
If I understand your requirement correctly, you could simply write the function template like this:
template<int A, int B, int Z = 1>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
return Matrix<A,B+Z>{};
}
and then use it like this:
Matrix<1,2> a = addZCols(Matrix<1,1>{});
Matrix<1,4> b = addZCols<1,1,3>(Matrix<1,1>{});
By default, the 3rd parameter is 1, and so this function template can be used as addOneCol.
As #Evg points out, template parameters have the nice property that default arguments can appear in any order, so we could have the Z argument in the first position:
template<int Z = 1, int A, int B>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
return Matrix<A,B+Z>{};
}
This allows you to make the call more conveniently, like this:
Matrix<1,2> a = addZCols(Matrix<1,1>{});
Matrix<1,4> b = addZCols<3>(Matrix<1,1>{});
Since only Z needs to be specified, as A, and B can be deduced from the Matrix argument.
There may be a more efficient approach, but with the recursion solution that you proposed, SFINAE can be used to disambiguate the two versions of the template function.
#include <type_traits>
template <int A, int B>
struct Matrix {
constexpr int rows() const { return A; }
constexpr int cols() const { return B; }
int data;
};
template<int Z, int A, int B, std::enable_if_t<Z == 0, int> = 0>
Matrix<A, B> addZCols(Matrix<A,B> m1) {
return m1;
}
template<int Z, int A, int B, std::enable_if_t<Z != 0, int> = 0>
Matrix<A,B+Z> addZCols(Matrix<A,B> m1) {
return addOneCol(addZCols<Z-1, A, B>(m1));
}
template<int A, int B>
Matrix<A,B+1> addOneCol(Matrix<A,B> m1) {
return Matrix<A,B+1>();
}
int main() {
Matrix<2, 2> m1;
auto m2 = addZCols<3>(m1);
static_assert(m2.rows() == 2, "check rows");
static_assert(m2.cols() == 5, "check cols");
return 0;
}
I have also offset the recursion limit by one for clarity and re-ordered the template parameters of addZCols to make it nicer to call, but it works just the same with your original signature.

C++14: How to group variadic inputs by template parameter?

Say I have two classes:
template <unsigned N>
class Pixel {
float color[N];
public:
Pixel(const std::initializer_list<float> &il)
{
// Assume this code can create a Pixel object from exactly N floats, and would throw a compiler error otherwise
}
};
template <unsigned N>
class PixelContainer {
std::vector<Pixel<N>> container;
};
What I'm trying to do is to write a constructor for PixelContainer such that:
It would instantiate correctly for the following cases (example, not exhaustive):
PixelContainer<3> pc1(1, 2, 3) // Creates a container containing one Pixel<3> objects
PixelContainer<3> pc2(1, 2, 3, 4, 5, 6) // Creates a container containing 2 Pixel<3> objects
PixelContainer<2> pc3(1, 2, 3, 4, 5, 6) // Creates a container containing 3 Pixel<2> objects
It would not compile for the following cases (as example, not exhaustive):
PixelContainer<3> pc4(2, 3) // Not enough arguments
PixelContainer<2> pc5(1, 2, 3, 4, 5) // Too many arguments
How do I achieve the above using template meta-programming? I feel it should be achievable, but can't figure out how. Specifically, I do not want to be doing the grouping myself e.g
PixelContainer<2> pc2({1, 2}, {3, 4}, {5, 6}) // Creates a container containing 3 Pixel<2> objects
(See this question for the inspiration behind mine)
template<class T, std::size_t I, std::size_t...Offs, class Tup>
T create( std::index_sequence<Offs...>, Tup&& tup ) {
return T( std::get<I+Offs>(std::forward<Tup>(tup))... );
}
template <unsigned N>
struct Pixel {
float color[N];
template<class...Ts,
std::enable_if_t< sizeof...(Ts)==N, bool > = true
>
Pixel(Ts&&...ts):color{ std::forward<Ts>(ts)... } {};
};
template <unsigned N>
struct PixelContainer {
std::vector<Pixel<N>> container;
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<std::decay_t<T0>, PixelContainer>{}, bool> =true
>
PixelContainer(T0&& t0, Ts&&...ts):
PixelContainer( std::make_index_sequence<(1+sizeof...(Ts))/N>{}, std::forward_as_tuple( std::forward<T0>(t0), std::forward<Ts>(ts)... ) )
{}
PixelContainer() = default;
private:
template<class...Ts, std::size_t...Is>
PixelContainer( std::index_sequence<Is...>, std::tuple<Ts&&...>&& ts ):
container{ create<Pixel<N>, Is*N>( std::make_index_sequence<N>{}, std::move(ts) )... }
{}
};
create takes a type, a starting index, and a index sequence of offsets. Then it takes a tuple.
It then creates the type from the (starting index)+(each of the offsets) and returns it.
We use this in the private ctor of PixelContainer. It has an index sequence element for each of the Pixels whose elements are in the tuple.
We multiply the index sequence element by N, the number of elements per index sequence, and pass that to create. Also, we pass in an index sequence of 0,...,N-1 for the offsets, and the master tuple.
We then unpack that into a {} enclosed ctor for container.
The public ctor just forwards to the private ctor with the right pack of indexes of one-per-element (equal to argument count/N). It has some SFINAE annoyance enable_if_t stuff to avoid it swallowing stuff that should go to a copy ctor.
Live example.
Also,
std::enable_if_t<0 == ((sizeof...(Ts)+1)%N), bool> =true
could be a useful SFINAE addition to PixelContainer's public ctor.
Without it, we simply round down and discard "extra" elements passed to PixelContainer. With it, we get a "no ctor found" if we have extra elements (ie, not a multiple of N).
Made something as well, which relies more on compiler optimizations for performance than #Yakk's answer.
It uses temporary std::arrays. temp is used to store the passed values somewhere. temp_pixels is used to copy pixel data from temp. Finally temp is copied into container.
I believe that those arrays do get optimized away, but I'm not certain. Looking at godbolt it seems that they are but I am not good at reading compiler assembly output :)
#include <array>
#include <algorithm>
#include <cstddef>
#include <vector>
template <unsigned N>
struct Pixel {
float color[N]; // consider std::array here
};
template <unsigned N>
class PixelContainer {
std::vector<Pixel<N>> container;
public:
template<class... Ts>
PixelContainer(Ts... values)
{
static_assert(sizeof...(Ts) % N == 0, "Pixels should be grouped by 3 values in PixelContainer constructor");
const std::array<float, sizeof...(Ts)> temp{float(values)...};
std::array<Pixel<N>, sizeof...(Ts) / N> temp_pixels{};
for (std::size_t i = 0; i < sizeof...(Ts); i += N)
{
auto& pixel = temp_pixels[i / N];
std::copy(
temp.begin() + i, temp.begin() + i + N,
pixel.color
);
}
container = std::vector<Pixel<N>>(temp_pixels.begin(), temp_pixels.end());
}
};
int main()
{
PixelContainer<3> pc1(1, 2, 3); // Creates a container containing one Pixel<3> objects
PixelContainer<3> pc2(1, 2, 3, 4, 5, 6); // Creates a container containing 2 Pixel<3> objects
PixelContainer<2> pc3(1, 2, 3, 4, 5, 6); // Creates a container containing 3 Pixel<2> objects
/*
PixelContainer<3> pc4(2, 3); // Not enough arguments
PixelContainer<2> pc5(1, 2, 3, 4, 5); // Too many arguments
*/
}
I would propose some hybrid version, there is still a temporary array, but no temporary for pixels
template <unsigned N>
struct PixelContainer {
template<std::size_t S, std::size_t... elts>
auto createOne(const std::array<float, S> &arr, std::size_t offset,
std::index_sequence<elts...>) {
return Pixel<N>{ arr[offset + elts]... };
}
template<typename... Floats>
PixelContainer(Floats... vals) {
static_assert(sizeof...(vals) % N == 0, "invalid number of args");
std::array<float, sizeof...(vals)> arr = { float(vals)... };
for (size_t i = 0; i < sizeof...(vals) / N; i++) {
container.push_back(createOne(arr, i * N, std::make_index_sequence<N>{}));
}
}
std::vector<Pixel<N>> container;
};
I think the answer is pretty much given in the link you provided (C++ number of function's parameters fixed by template parameter). You just need to change the assert there: instead of sizeof...(Floats) == N you'll want sizeof...(Floats) % N == 0.

Ambiguous constructor call (I assume)

I have a matrix class defined like so:
template <typename T, unsigned int N, unsigned int M>
class TMatrixNxM //Rows x columns
{
public:
TMatrixNxM(T = T(0)); //Default constructor
TMatrixNxM(const std::array<std::array<T, M>, N>&); //Construct from array
TMatrixNxM(std::initializer_list<std::initializer_list<T>>); //Initializer
//...
private:
std::array<std::array<T, M>, N> data; //ROW-MAJOR
};
Now, in the code which uses matrices I have:
Math::Matrix3x3 b({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
(Note: Matrix3x3 is a typedef for TMatrixNxM< float, 3, 3> and also, it is in a Math namespace)
Until now, it worked, because I didn't always have that array constructor, only initializer list one. But now, however, the compiler doesn't even finish compiling, it crashes! (I get "stopped working" popup and I have to close it, I am using MS VS Express 2013)
If I do it like this:
Math::Matrix3x3 b = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Then it works fine. This is what I assume:
When I do it like that, then there is no ambiguity since you can only call the initializer_list constructor that way. With the first approach the compiler may be confused, since array is an aggregate type which means the initialization starts with double braces like: {{...}}, but also since I have an initializer list of initializer lists I have to use double braces. But should it really be a problem, since I never actually do double braces, those are single-braced lists inside a larger single-braces list?
What is really happening here and how do I solve this problem?
Thank you for your time!
EDIT
Compiler doesn't crash anymore if I make the constructor take the array by const pointer (since I'm never really going to straight-out plop the array in the constructor call, I have initializer list for that):
TMatrixNxM(const std::array<std::array<T, M>, N>*);
However can someone explain what was an actual problem before, was my assumption right?
Here's a minimal compilable (or, well, not) code that you can use to test:
#include <array>
#include <initializer_list>
template <typename T, unsigned int N, unsigned int M>
class TMatrixNxM //Rows x columns
{
public:
TMatrixNxM(T = T(0));
TMatrixNxM(const std::array<std::array<T, M>, N>&);
TMatrixNxM(std::initializer_list<std::initializer_list<T>>);
private:
std::array<std::array<T, M>, N> data; //ROW-MAJOR
};
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(T par_value)
{
std::array<T, M> temp;
temp.fill(par_value);
data.fill(temp);
}
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(const std::array<std::array<T, M>, N> &par_values)
{
data = par_values;
}
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(std::initializer_list<std::initializer_list<T>> par_values)
{
int i = 0;
for(std::initializer_list<T> row : par_values)
{
int j = 0;
for(T value : row)
{
data[i][j] = value;
++j;
}
++i;
}
}
int main()
{
TMatrixNxM<float, 3, 3> b({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
return 0;
}
If you comment out the constructor declaration/definition with arrays then it should compile and execute fine. As stated previously, I am using compiler from MS VS Express 2013.
Well, looks like it just doesn't work on Microsoft's compiler. I tried to compile the same code on my Linux machine that uses GCC and it compiles with no problems. And it is indeed calling the initializer list constructor as it should. If I make an array in a variable and pass it, it also compiles fine, and it calls the array constructor:
std::array<float, 3> a = {{1, 2, 3}};
std::array<std::array<float, 3>, 3> b = {{a, a, a}};
TMatrixNxM<float, 3, 3> mat(b);
It also compiles and calls the array constructor if I initialize the array directly:
TMatrixNxM<float, 3, 3> mat({{ {{1, 2, 3}}, {{4, 5, 6}}, {{7, 8, 9}} }});