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.
Related
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.
I am revisiting C++ after a long hiatus, and I would like to use templates to design the known "map" function -- the one which applies a function to every element of a collection.
Disregarding the fact my map doesn't return anything (a non-factor here), I have managed to implement what I wanted if the function passed to "map" does not need to accept additional arguments:
#include <iostream>
template <typename C, void fn(const typename C::value_type &)> void map(const C & c) {
for(auto i : c) {
fn(i);
}
}
struct some_container_type { /// Just some hastily put together iterable structure type
typedef int value_type;
value_type * a;
int n;
some_container_type(value_type * a, int n): a(a), n(n) { }
value_type * begin() const {
return a;
}
value_type * end() const {
return a + n;
}
};
void some_fn(const int & e) { /// A function used for testing the "map" function
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, some_fn>(sc);
}
However, I would like map to accept additional arguments to call fn with. I've tried to compile the modified variant of the program (container type definition was unchanged):
template <typename C, typename ... T, void fn(const typename C::value_type &, T ...)> void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
void some_fn(const int & e, int a, float b, char c) {
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, int, float, char, some_fn>(sc, 1, 2.0f, '3');
}
But gcc -std=c++20 refuses to compile the modified program containing the above variant, aborting with:
<source>: In function 'int main()':
<source>:29:56: error: no matching function for call to 'map<some_container_type, int, float, char, some_fn>(const some_container_type&, int, int, int)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:16:97: note: candidate: 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
16 | template <typename C, typename ... T, void fn(const typename C::value_type &, T ... args)> void map(const C & c, T ... args) {
| ^~~
<source>:16:97: note: template argument deduction/substitution failed:
<source>:29:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:29:56: note: expected a type, got 'some_fn'
Microsoft Visual C++ compiler (19.24.28314) gives a more descriptive error message:
error C3547: template parameter 'fn' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'map'
Can someone explain if and how I can idiomatically accomplish for map to accept arbitrary arguments for forwarding these to fn?
I know I can pass fn to the map function as argument instead of specifying it as an argument to the template, but for reasons related to inlining and to better understand C++ templates, I'd like to retain fn a template rather than a function parameter.
I also don't want to use any libraries, including the standard library (what use of std I show in the examples above is only for clarifying the question). I know there are "functor" and "forward" somewhere in the libraries, but I suppose they too were written in C++, so I am curious if my problem can be solved without any libraries.
A simple way to fix this would be to deduce the non-type template parameter for the function, and reorder the template parameter list
template <typename C, auto fn, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
and then call it like this
map<some_container_type, some_fn, int, float, char>(sc, 1, 2.0f, '3');
Here's a demo
You could also move fn to the beginning of the template parameter list.
template <auto fn, typename C, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
Now since C and T can be deduced from the function arguments, this makes the call site much cleaner
map<some_fn>(sc, 1, 2.0f, '3');
Here's a demo
Could someone explain the difference between the two function calls here?
When can you pass a templated variable to a function?
In both cases I should end up with an templated array inside the function, but only one compiles.
template<int DIM>
struct MyStruct
{
array<int, DIM> structArr;
};
template<int DIM> void testA( MyStruct <DIM>& myStruct) { }
template<int DIM> void testB( array<int, DIM>& arrA) { }
int main()
{
MyStruct<3> myStruct;
array<int, 3> arr;
testA(myStruct);
testB(arr); //compile error
return 0;
}
EDIT:
Error messages look like this:
error: no matching function for call to ‘testB(std::array&)’
testB(arr); //compile error
^
note: candidate: template void testB(std::array&)
template<int DIM> void testB( array<int, DIM>& arrA) { }
^~~~~
note: template argument deduction/substitution failed:
note: mismatched types ‘int’ and ‘long unsigned int’
The template parameter for the size of a std::array is of type std::size_t. However, here you require supplying an int for DIM in the function definition. This is probably an issue for the template deduction rules.
I am interested in printing out a variadic number of templated arguments.
So far I have implemented the code below but run into the following compile-time errors
./include/AView.hpp:46:5: error: call to member function 'indexcalc2' is ambiguous
indexcalc2<Strs...>();
^~~~~~~~~~~~~~~~~~~
./include/AView.hpp:46:5: note: in instantiation of function template specialization 'myView<double, 2, 1, 2, 3, 4,
5>::indexcalc2<4, 5>' requested here
./include/AView.hpp:46:5: note: in instantiation of function template specialization 'myView<double, 2, 1, 2, 3, 4,
5>::indexcalc2<3, 4, 5>' requested here
./include/AView.hpp:52:5: note: in instantiation of function template specialization 'myView<double, 2, 1, 2, 3, 4,
5>::indexcalc2<2, 3, 4, 5>' requested here
indexcalc2<Strides...>();
^
main.cpp:23:9: note: in instantiation of member function 'myView<double, 2, 1, 2, 3, 4, 5>::indexcalc' requested here
A.indexcalc();
^
./include/AView.hpp:36:8: note: candidate function [with Str0 = 5]
void indexcalc2() const
^
./include/AView.hpp:43:8: note: candidate function [with Str0 = 5, Strs = <>]
void indexcalc2() const
The idea here is to create a layout struct with a get method that will output the first template argument stride. I then created a second struct called view with an indexcalc method which would generate a layout, print the stride, and recursively peal template arguments by calling indexcalc2.
Unfortunately, I don't quite have the correct implementation and wondering if there might be suggestions.
template<size_t... Strides>
struct layout
{
static size_t get(size_t idx0) {return idx0;};
};
template<size_t stride0, size_t... Strides>
struct layout<stride0, Strides...> : layout<Strides...>
{
static size_t get()
{
std::cout<<"Stride is : "<<stride0<<std::endl;
return stride0;
}
};
template<typename T, size_t DIM,size_t Stride0, size_t... Strides>
struct myView
{
myView() {};
template<size_t Str0>
void indexcalc2() const
{
std::cout<<layout<Str0>::get()<<std::endl;
std::cout<<"End"<<std::endl;
}
template<size_t Str0, size_t... Strs>
void indexcalc2() const
{
std::cout<<layout<Str0,Strs...>::get()<<std::endl;
indexcalc2<Strs...>();
}
void indexcalc() const
{
std::cout<<layout<Stride0, Strides...>::get()<<std::endl;
indexcalc2<Strides...>();
}
};
int main()
{
struct myView<double,2,1,2,3,4,5> A;
A.indexcalc();
return 0;
}
You have multiple problems here:
template<size_t Str0> void indexcalc2() const;
template<size_t Str0, size_t... Strs> void indexcalc2() const;
will result in the same instance if Strs contains no elements. So you have an ambiguous instance here which is already complained by the compiler:
./include/AView.hpp:46:5: error: call to member function 'indexcalc2' is ambiguous
indexcalc2<Strs...>();
So the idea by unwinding a parameter set is always to have an empty specialization like:
template<> void indexcalc2() const;
so that a call from
indexcalc2<Strs...>();
goes to that specialization if Strs is empty.
But after this you will run into the next trouble:
Specialization of templates can not be done in the class scope. So you must specialize outside like:
template<> void myView::indexcalc2<>() const;
But your myView is also a template and it is not allowed to specialize your method without fully specialize the class parameters!
If you go with c++17 you simply can use constexpr ifto get rid of all that problems, but in c++11 you have to go over specializing with an encapsulating class.
Your templated class with templated methods is to complex to simply write down a complete solution here for you.
I'm writing two functions with the same name (and with similar parameters):
The first one only takes a variable amount of integer parameters (minimum one).
The second one takes a variable amount of struct coordinate as parameters (again minimum one).
The struct coordinate can be constructed from both int and std::vector<int>, so the second one can in that way also take int and std::vector<int>.
The problem is that if the first parameter to the second function is an int then the compilation fails.
Is it possible to make the third line in main() call the other tmp function?
My current implementation is listed below.
int main(int argc, char** argv)
{
tmp(1, 2, 3, 4, 5); // OK! First function calls 'single'
tmp(std::vector<int>{1, 2, 3}, 4, 5); // OK! Second function calls 'multi'
tmp(1, std::vector<int>{2, 3}, 4); // Compilation error! First function calls 'single'
return 0;
}
Compiler output:
prog.cpp: In instantiation of ‘std::vector<int> tmp(int, types ...) [with types = {std::vector<int, std::allocator<int> >, int}]’:
prog.cpp:58:34: required from here
prog.cpp:29:23: error: no matching function for call to ‘single(std::vector<int>&, std::vector<int>&, int&)’
single(list, ints ...);
^
prog.cpp:29:23: note: candidates are:
prog.cpp:12:6: note: void single(std::vector<int>&, int)
void single(std::vector<int>& list, int first)
^
prog.cpp:12:6: note: candidate expects 2 arguments, 3 provided
prog.cpp:18:6: note: template<class ... types> void single(std::vector<int>&, int, types ...)
void single(std::vector<int>& list, int first, types ... ints)
^
prog.cpp:18:6: note: template argument deduction/substitution failed:
prog.cpp:29:23: note: cannot convert ‘ints#0’ (type ‘std::vector<int>’) to type ‘int’
single(list, ints ...);
^
My current implementation:
#include <iostream>
#include <vector>
struct coordinate
{
std::vector<int> c;
coordinate(std::vector<int> A) : c(A) {}
coordinate(int A) : c{A} {}
};
// Function to end the recursive call to single
void single(std::vector<int>& list, int first)
{
list.push_back(first);
}
// Recursive function to store the parameters (only int)
template <typename... types>
void single(std::vector<int>& list, int first, types ... ints)
{
list.push_back(first);
single(list, ints ...);
}
// 'First' function
template <typename... types>
std::vector<int> tmp(int i, types ... ints)
{
std::vector<int> list;
list.push_back(i);
single(list, ints ...);
return list;
}
// Function to end the recursive call to multi
void multi(std::vector<std::vector<int> >& list, coordinate first)
{
list.push_back(first.c);
}
// Recursive function for storing the parameters (only 'coordinate')
template <typename... types>
void multi(std::vector<std::vector<int> >& list, coordinate first, types ... coords)
{
list.push_back(first.c);
multi(list, coords ...);
}
// 'Second' function
template <typename... types>
std::vector<std::vector<int> > tmp(coordinate i, types ... coords)
{
std::vector<std::vector<int> > list;
list.push_back(i.c);
multi(list, coords ...);
return list;
}
Alright, based on my new understanding from the comment you want something like this:
template <typename... Args>
typename std::enable_if<all_ints<Args...>::value>::type
tmp(Args... args) {
// the all integer version
}
template <typename... Args>
typename std::enable_if<!all_ints<Args...>::value>::type
tmp(Args... args) {
// the coordinate version
}
Then you just need to write the type trait to check if everything is an integer.
template <typename... T>
struct all_ints : std::true_type { };
template <typename T, typename... Rest>
struct all_ints<T, Rest...>
: std::integral_constant<bool,
std::is_integral<T>::value && all_ints<Rest...>::value>
{ }
I used std::is_integal to handle all the integer types. If you really want explicitly int, you can fix it.