How to pass an n-dim Eigen tensor to a function? - c++

I'm looking to make a loss function that can take 2 tensors of any dimensions as parameters, but the dimensions of tensor 1 (t1) and tensor (t2) must match. Below are the templates that I tried to use to can pass the tensors into the function. I was thinking that T would be a type and N would model the number of indexes possible without explicitly writing a type for infinitely possible tensor dimensions.
loss.h
#include <iostream>
namespace Loss {
template<class T, std::size_t N>
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
std::cout << "Loss::loss() not implemented" << std::endl;
};
};
main.cpp
#include "loss.h"
int main() {
Eigen::Tensor<double, 3> t1(2, 3, 4);
Eigen::Tensor<double, 3> t2(2, 3, 4);
t1.setZero();
t2.setZero();
Loss::loss(t1, t2);
return 0;
}
The type error that I get before compiling from my editor:
no instance of function template "Loss::loss" matches the argument list -- argument types are: (Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>, Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>
And this is the message I get once I compile (unsuccessfully):
note: candidate template ignored: substitution failure [with T = double]: deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'std::size_t' (aka 'unsigned long'))
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
^
1 error generated.

The error message is pointing out the type of the non-type template parameter is size_t, but in the declaration of t1 and t2 the value of that parameter is 3, which has type int. This mismatch makes the template argument deduction fail.
You can fix this by changing the type of the non-type template parameter to int
template<class T, int N>
void loss( // ...
or just let it be deduced
template<class T, auto N>
void loss( // ...

number literals are signed integers, and you’ve specified the number type of your template as size_t Which is unsigned. So the types don’t match. Try Eigen::Tensor<double, 3u> … in your main program to use unsigned literals.

Related

Why auto is not allowed in template function for creating a built-in array?

This code below does not compile:
template<typename... Ts>
void CreateArr(const Ts&... args)
{
auto arr[sizeof...(args) + 1]{ args... };
}
int main()
{
CreateArr(1, 2, 3);
}
due to the following errors:
'arr': in a direct-list-initialization context, the type for 'auto [6]' can only be deduced from a single initializer expression
auto [6]': an array cannot have an element type that contains 'auto'
'initializing': cannot convert from 'const int' to 'std::initializer_list<int>'
My questions are:
Why cannot I use auto to define the type of the array?
How to define it properly to work with the template?
Why cannot I use auto to define the type of the array?
For the same reason, following does not work/ allowed!
auto ele[]{ 1, 2, 3 };
More reads: Why can't I create an array of automatic variables?
How to define it properly to work with the template?
Use the std::common_type_t for specifying the type
#include <type_traits> // std::common_type_t
template<typename... Ts>
void CreateArr(const Ts&... args)
{
std::common_type_t<Ts...> arr[sizeof...(args)]{ args... };
static_assert(std::is_array_v<int[sizeof...(args)]>, "is not array!");
}
(See a Live Demo)

Error: "couldn't infer template argument '_Tp' " when passing in {} into

struct compar {
bool operator()(const vector<int>& a,
const vector<int>& b) const {
return a[1] < b[1];
}
};
...
auto it = lower_bound(events.begin(), events.end(), {0, events[i][0]}, compar());
This code gives me an error with the {0, events[i][0]}:
/bits/stl_algo.h:2022:5: note: candidate template ignored: couldn't infer template argument '_Tp'
lower_bound(_ForwardIterator __first, _ForwardIterator __last,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/algorithmfwd.h:353:5: note: candidate function template not viable: requires 3 arguments, but 4 were provided
lower_bound(_FIter, _FIter, const _Tp&);
^
1 error generated.
But when I define it as a vector explicitly, it works as desired.
vector<int> point = {0, events[i][0]};
auto it = lower_bound(events.begin(), events.end(), point, compar());
Can someone explain why?
Braced-init-list has no type itself, it can't be used for deduction of template parameter. This is non-deduced context:
In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
The parameter P, whose A is a braced-init-list, but P is not std::initializer_list, a reference to one (possibly cv-qualified), or a reference to an array:
As you're showed, you have to specify the type explicitly, e.g.
auto it = lower_bound(events.begin(), events.end(), vector<int>{0, events[i][0]}, compar());
// ^^^^^^^^^^^
Or specify template arguments as
auto it = lower_bound<decltype(events.begin()), std::vector<int>>(events.begin(), events.end(), {0, events[i][0]}, compar());
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

incorrect deduction of template parameter pack

The following program does not compile:
template <unsigned int dim, unsigned int N, bool P, bool C, class... ParametersType>
void test(ParametersType&&... par)
{
}
int main()
{
test<2, 3, true, false>(2, 1, {8, 8});
}
See it live on Coliru.
The error message
g++ -std=c++17 -O1 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:8:41: error: too many arguments to function 'void test(ParametersType&& ...)
[with unsigned int dim = 2; unsigned int N = 3; bool P = true; bool C = false; ParametersType = {}]'
8 | test<2, 3, true, false>(2, 1, {8, 8});
| ^
main.cpp:2:6: note: declared here
2 | void test(ParametersType&&... par)
| ^~~~
indicates that the parameter pack ParametersType... is deduced to an empty one, while I would expect it to be deduced according to the types of the arguments passed to test.
The problem is in the {8, 8} parameter passed to test.
Explicitly passing a std::array to the function solves the problem:
#include <array>
template <unsigned int dim, unsigned int N, bool P, bool C, class... ParametersType>
void test(ParametersType&&... par)
{
}
int main()
{
test<2, 3, true, false>(2, 1, std::array<int, 2>{8, 8});
}
See it live on Coliru.
Why does the compiler apparently incorrectly deduces the pack in the first example?
If the compiler is not able to deduce {8, 8} to an std::array, I would expect an "impossible to deduce" error. Why instead does the compiler deduce the pack to an empty one?
Template errors are hard to get right. It's just a quality of implementation. Clang for instances gives
main.cpp:2:6: note: candidate template ignored: substitution failure
[with dim = 2, N = 3, P = true, C = false]: deduced incomplete pack <int, int, (no value)>
for template parameter 'ParametersType'
which is easier to understand. And yes, unless using auto, {stuff} has no type.
From cppreference:
A braced-init-list is not an expression and therefore has no type,
e.g. decltype({1,2}) is ill-formed. Having no type implies that
template type deduction cannot deduce a type that matches a
braced-init-list, so given the declaration template void
f(T); the expression f({1,2,3}) is ill-formed.
You can also use auto in this context to fix your issue:
template <unsigned int dim, unsigned int N, bool P, bool C, class... ParametersType>
void test(ParametersType&&... par)
{
}
int main()
{
auto x = { 8, 8 };
test<2, 3, true, false>(2, 1, x);
}

unpacking values of an array as parameters to a variadic function

I am trying (at compile time) to unpack integers as arguments to a variadic function. The idea would be to have those values packed in an array or in a std::index_sequence (c++14) at compile time. I have tried to use some of the answers from older posts, but I find the example code unreadable for my level.
Here is a simple example with the functionality that I need to implement in a code that I am writing, in this case attempting to use std::make_index_sequence. I do not necessarily need to use the latter. The problem is that the values of the sequence are not unpacked as arguments to the variadic function:
#include <cstdio>
#include <iostream>
#include <utility>
using namespace std;
void print(const int &val){
cout << val << endl;
}
template<typename ...S> void print(const int &val, const S&... others)
{
print(val);
print(others...);
}
template<size_t n> void printNumbers(){
std::make_index_sequence<n> a;
print(a);
}
int main(){
printNumbers<6>();
}
The output from GCC8:
tet.cc: In instantiation of ‘void printNumbers() [with long unsigned int n = 6]’:
tet.cc:25:19: required from here
tet.cc:20:8: error: no matching function for call to ‘print(std::make_index_sequence<6>&)’
print(a);
~~~~~^~~
tet.cc:8:6: note: candidate: ‘void print(const int&)’
void print(const int &val){
^~~~~
tet.cc:8:6: note: no known conversion for argument 1 from ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’} to ‘const int&’
tet.cc:12:30: note: candidate: ‘template<class ... S> void print(const int&, const S& ...)’
template<typename ...S> void print(const int &val, const S&... others)
^~~~~
tet.cc:12:30: note: template argument deduction/substitution failed:
tet.cc:20:9: note: cannot convert ‘a’ (type ‘std::make_index_sequence<6>’ {aka ‘std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5>’}) to type ‘const int&’
std::make_index_sequence<6> is an alias for:
std::integer_sequence<std::size_t, 0, 1, 2, 3, 4, 5>
and this is the type of the expression a that is the argument of the function call print(a). Your print function expects individual values, not an std::integer_sequence.
To make your implementation work, you should first deduce the indices, and only then use them as arguments for print:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
print(Is...);
}
template <std::size_t N>
void printNumbers()
{
printNumbers(std::make_index_sequence<N>{});
}
In c++17 you could remove the intermediate print function and just say:
template <std::size_t... Is>
void printNumbers(std::index_sequence<Is...>)
{
(print(Is), ...);
}
In c++20 you can both create an index sequence and deduce its indices within a single function:
template <std::size_t N>
void printNumbers()
{
[] <std::size_t... Is> (std::index_sequence<Is...>)
{ (print(Is), ...); }(std::make_index_sequence<N>{});
}
DEMO
As an addendum to the Piotr Skotnicki's answer, I propose a C++14 way to avoid the recursive print().
Not so elegant as the C++17 solution, based on template folding, but equally permit to avoid the use of recursion (and consider that template recursion is usually strict limited by compilers, so the recursive solution works but not when N exceed the recursion limit).
You have to write the printNumber() function as usual, passing a std::make_index_sequence<N> (that inherit from std::index_sequence<0, 1, ...., N-1> aka std::integer_sequence<std::size_t, 0, 1, ..., N-1>) to another function
template <std::size_t N>
void printNumbers ()
{ printNumbers2(std::make_index_sequence<N>{}); }
but in the printNumbers2() you can avoid to call the recursive print() and you can call the print() that effectively call std::cout inside the initialization of an unused array
template <std::size_t ... Is>
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (print(Is), 0)... };
}
You can also avoid both print() functions printing directly in printNumbers2()
void printNumbers2 (std::index_sequence<Is...>)
{
using unused = int[];
(void)unused { 0, (std::cout << val << std::endl, 0)... };
}
You can do the same in the C++17/C++20 template folding solutions.
In C++11 this solution doesn't works but only because std::make_integer_sequence and std::index_sequence are introduced from C++11.
If you write a C++11 surrogate for std::make_integer_sequence and std::index_sequence, you can adapt this solution also to C++11.

Template deduction, is this conforming?

It seems like this code is not correct as I get compiler errors for it. I'm trying to understand why:
template <class ... Ts>
struct type_list{};
template <class ... Ts, class T_, class ... Ts_>
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
return sizeof(T_);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}
clang produces the follow error:
example.cpp:16:16: error: no matching function for call to 'foo'
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
^~~
example.cpp:11:6: note: candidate template ignored: deduced conflicting types for parameter 'Ts'
(<int> vs. <>)
auto foo(type_list<Ts...>, Ts&&..., T_&&t, Ts_&&...) {
It seems to me that in my call Ts should be deduced to int (since that's the only way to make the first argument work out), and then everything else will be forced as a result. Why doesn't this work?
Clang deduces Ts from two conflicting sources: type_list<Ts...> and Ts&&.... Because you call foo with type_list<int>{}, Ts is first deduced as {int}. However, because the last argument Ts_&&... has the type of a parameter pack, it catches all that it can; in this case, the last two arguments (while T_&&t is passed the 5), which deduces Ts_ as {double, int} (and T_ as int). This leaves Ts with no argument, so it's deduced as {}.
Hence Clang's error message: Ts is deduced both as {} and {int} (the (<int> vs. <>) in the error message).
Note that GCC does things differently:
error: too few arguments to function 'auto foo(type_list, Ts&& ..., T_&&, Ts_&& ...) [with Ts = {int}; T_ = int; Ts_ = {double, int}]'
It tries to keep Ts deduced as {int} but still deduces Ts_ as {double, int}, and so there is no way to give the correct number of arguments.
To explain a bit further, consider the following:
template <class ... Ts>
struct type_list{};
template <class ... Ts>
auto foo(type_list<Ts...>, Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Clang correctly calls this out because the two deductions are conflicting:
note: candidate template ignored: deduced conflicting types for parameter 'Ts' (<int> vs. <int, double, int>)
Trouble is, you can't catch 2 of the arguments with another parameter pack, because it will either catch everything if placed after (which is your case), or nothing if placed before:
template <class ... Ts>
struct type_list{};
template <class... Ts, class... Ts_>
auto foo(type_list<Ts...>, Ts_..., Ts...) {
return sizeof...(Ts);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
}
Here, Clang produces the same error message because Ts is still deduced as both {int} from the type_list, and {int, double, int} by catching all the remaining arguments.
I think your only choices are to somehow deal with tuples or be more explicit with the call: foo<int>(type_list<int>{}, 5, 5.0, 3) (and you can probably remove the type_list in this case). That way, Ts isn't deduced anymore: you explicitly make it {int}.
C++ doesn't try every possibility and find the only one that is legal. It follows very specific rules, and if those rules don't work, it generates an error.
In general, a deduced ... pack must be last if it is deduced. Attempts to work around it will fail.
Here is a solution that avoids your problem:
template <class ... Ts, class...Us>
auto foo(type_list<Ts...>, Us&&...us) {
return [](Ts&&...ts, auto&& t, auto&&...) {
return sizeof(t);
}(std::forward<Us>(us)...);
}
int main() {
std::cerr << foo(type_list<int>{}, 5, 5.0, 3);
return 0;
}