I'm using a template library and since in my code one of the template parameters can assume a finite range of values, I decided to use, under suggestion, std::variant and declare in it all the objects I will possibly need:
std::variant<TemplateClass<1>, TemplateClass<2>, ..., TemplateClass<5>>
I never used this utility.
To access the methods of TemplateClass I have to use std::visit, but sometimes it works and other it doesn't, say no member function XXX in std::variant < .... > or "In instantiation of function template specialization ... " [I don't even understand what is the problem here]
Specifically, I'm using Eigen::Tensor library and when I call methods like rank(), dimension(n), it works, while for methods like dimensions() and setRandom() it doesn't.
Below a draft of my implementation
std::variant<Eigen::Tensor<double, 1>, Eigen::Tensor<double, 2>, /* ... */> makeTensor(
int i, const std::initializer_list<int> dims) {
switch (i) {
case 1: {
Eigen::Tensor<double, 1> T1;
T1.resize(dims);
return T1;
}
case 2: {
Eigen::Tensor<double, 2> T2;
T2.resize(dims);
return T2;
}
/* ... */
}
}
int main() {
auto myTensor{makeTensor(2, {4, 5})}; // Tensor 2D 4x5
// Working methods
auto rnk = std::visit([](const auto &tensor) { return tensor.rank(); }, myTensor);
auto dim1 = std::visit([](const auto &tensor) { return tensor.dimension(0); }, myTensor);
// Not working methods
auto dimsTens =
std::visit([](const auto &tensor) { return tensor.dimensions(); }, myTensor); // 5 times same error saying
//'In instantiation of function template specialization 'std::visit<(lambda at
/// home/virginie/Desktop/Project/main.cpp:62:33),
// std::variant<Eigen::Tensor<double, 1, 0, long>, Eigen::Tensor<double, 2, 0, long>, Eigen::Tensor<double, 3, 0,
// long>, Eigen::Tensor<double, 4, 0, long>, Eigen::Tensor<double, 5, 0, long>> &>''
std::visit([&myTensor]() { myTensor.setRandom(); }); // 'No member setRandom() in std::variant<...>'
}
Am I using std::visit in the wrong way?
---- EDIT ----
After the suggestion of #florestan, I have solved the problem related to dimensions(), while with setRandom I get the following:
In file included from /..../ main.cpp
required from here
You need to return the same type for all possible alternatives. In case of dimension, you need to copy the array elements to a vector, for example.
Something like this should help:
auto dimsTens=std::visit(
[](const auto &tensor) {
auto dims = tensor.dimensions();
return std::vector<int>(dims.begin(), dims.end());
}, myTensor);
The second error is because you don't call std::visit the right way. It needs two params, first the function and second the variant to visit. The following should work.
std::visit([](auto& t){ t.setRandom();}, myTensor);
Live code:
https://godbolt.org/z/vq4PYo
Related
Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }
If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));
You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}
Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.
I wanted to create a deep_flatten function template that would produce a range of elements that are deeply joined. For example, if we take into account only nested std::vectors, I can have:
template <typename T>
struct is_vector : public std::false_type { };
template <typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type { };
template <typename T>
auto deepFlatten(const std::vector<std::vector<T>>& vec) {
using namespace std::ranges;
if constexpr (is_vector<T>::value) {
auto range = vec | views::join;
return deepFlatten(std::vector(range.begin(), range.end()));
} else {
auto range = vec | views::join;
return std::vector(range.begin(), range.end());
}
}
This enables me to do:
std::vector<std::vector<std::vector<int>>> nested_vectors = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};
std::ranges::copy(
deep_flatten(nested_vectors),
std::ostream_iterator<int>(std::cout, " ")
);
which prints into the console the following text, as expected:
1 2 3 4 5 6 7 8 9 10 11 12 13
But, I don't like this solution that much. Not only it's inefficient (creating a number of temporary vectors), but it also works only with std::vectors. I figured that I could use some more of c++20 magic and use std::ranges::range concept:
namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
if constexpr (range<Rng>) {
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
This seemed to me pretty straightforward - we have a std::ranges::range and we inspect it's value type. Depending on whether it's a nested range, we recurse or simply return joined elements.
Sadly, it doesn't work. After trying to run:
int main() {
std::set<std::vector<std::list<int>>> nested_ranges = {
{{1, 2, 3}, {4, 5}, {6}},
{{7}, {8, 9}, {10, 11, 12}},
{{13}}
};
std::ranges::copy(
rng::deep_flatten(nested_ranges),
std::ostream_iterator<int>(std::cout, " ")
);
}
I get an error saying that:
In instantiation of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]':
required from 'auto rng::deep_flatten(Rng&&) [with Rng = std::set<std::vector<std::__cxx11::list<int> > >&]'
required from here
error: use of 'auto rng::deep_flatten(Rng&&) [with Rng = std::ranges::join_view<std::ranges::ref_view<std::set<std::vector<std::__cxx11::list<int> > > > >]' before deduction of 'auto'
39 | return deep_flatten(rng | views::join);
| ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
Having researched similar problems, I cannot really get why the error appears here.
I am using gcc version 10.1.0 (Rev3, Built by MSYS2 project)
There are two problems here.
The first problem is yours:
namespace rng {
template <std::ranges::range Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
if constexpr (range<Rng>) { // <==
return deep_flatten(rng | views::join);
} else {
return rng | views::join;
}
}
}
This function is infinitely recursive. deep_flatten is constrained range<Rng>, so the if constexpr check there is always going to be true, so we're never going to enter the base case. This is just a bug - we're checking the wrong thing, it's not if we're a range, it's if our underlying value is a range. That's:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
auto joined = rng | views::join;
if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
And here we get into the second problem, which is the standard library's problem. What rng | views::join means is:
The name views::join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views::join(E) is expression-equivalent to join_view{E}.
But join_view{E} for an E that's already a specialization of join_view... is a no-op right now because of class template argument deduction (CTAD) - the copy deduction candidate is the best candidate, so our nested join operation actually becomes a single join. Your original implementation gets around this problem because it's not join-ing a join_view, it's always join-ing vectors.
I've submitted LWG 3474.
In the meantime, we can work around the views::join problem by just directly using join_view and specifying the template argument explicitly:
namespace rng {
template <typename Rng>
auto deep_flatten(Rng&& rng) {
using namespace std::ranges;
auto joined = join_view<views::all_t<Rng>>(rng);
if constexpr (range<range_value_t<decltype(joined)>>) {
return deep_flatten(joined);
} else {
return joined;
}
}
}
This works.
In the C++ code below, what is type of a? typeid returns St16initializer_listIPKcE
auto a = { "lol", "life" };
When you have
auto a = { "lol", "life" };
The compiler will try to deduce a std::initializer_list where the type is what all of the elements are. In this case "lol" and "life" are both a const char[] so you have a std::initializer_list<const char*>.
If on the other have you had something like
auto foo = { 1, 2.0 };
Then you would have a compiler error since the element types are different.
The rules for auto deduction of intializer list are as follows with one expection
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
The expection is that before C++17
auto x5{ 3 };
is a std::intializer_list<int> where in C++17 and most compilers that already adopted the rule it is deduced as a int.
The answer to your question is std::intializer_list<char const*>
If you want to learn non-mangled name of a type, you can use the undefined template trick:
template<typename T>
void get_type_name(T&&);
then call it
auto a = { "", ""};
get_type_name(a);
You should get a readable error message stating something along the lines of
undefined reference to `void get_type_name<std::initializer_list<char const*>&>(std::initializer_list<char const*>&)'
I'm trying to initialize a class Vec with a brace-enclosed initializer list, to be called like in this minimal example:
int main()
{
Vec<3> myVec1{{1,2,3}}; // or: myVec1({1,2,3});
Vec<3> myVec2;
myVec2 = {1, 2, 3};
Vec<3> myVec3 = {1,2,3};
return 0;
}
All these initializations (and the assignment) should work. (Plus custom default constructor. Using an aggregate class is thus impossible.)
While I could just use a std::initializer_list like such:
template <unsigned C>
struct Vec
{
Vec(){}
Vec(std::initializer_list<int> list)
{
//does not work for obvious reasons:
//static_assert(list.size() == C, "");
}
};
I can't statically ensure the number of parameters to be equal to my template parameter, i.e., an initialization with {1, 2, 3, 4} would only fail during runtime.
Browsing SO, I came up with the following:
template <unsigned C>
struct Vec
{
Vec(){}
Vec(const unsigned(&other)[C]){}
Vec& operator=(const unsigned(&other)[C]){return *this;}
};
This works fine for the assignment and () or {} initialization (as for myVec1) - but it fails for the initialization using = (as for myVec3).
(GCC gives error "could not convert '{1, 2, 3}' from '' to 'Vec<3u>'")
I don't get why one of the initializations should work, but not the other.
Any other ideas how I can use the brace-enclosed initializer list, but also ensure the correct length at compile time?
Thanks :)
Initializer lists are more appropriate for situations where the size of the list is dynamic. In your case, where the size of Vec is a template parameter (i.e. static), you're probably better off with variadic parameters:
template <unsigned C>
struct Vec
{
Vec(){}
template<typename ... V>
Vec(V ... args)
{
static_assert(sizeof...(V) == C, "");
//...
}
};
then these will work:
Vec<3> myVec2;
myVec2 = {1, 2, 3};
Vec<3> myVec3 = {1,2,3};
But this one won't, as it explicitly requires an std::initializer_list:
Vec<3> myVec1{{1,2,3}};
You need two pair of brackets:
Vec<3> myVec3 = {{1,2,3}};
When you do:
Vec<3> myVec3 = {1, 2, 3};
// It is the same as (except that explicit constructor are not considered):
Vec<3> myVec3 = Vec<3>{1, 2, 3};'
So basically, the compiler will look for a constructor for Vec<3> which either takes an std::initializer_list or a constructor that takes 3 arguments, all of which being constructible from int.
If you want a std::array like structure, you need to make your type an aggregate, meaning that it should not have user-defined constructor:
template <unsigned N>
struct Vec {
int data[N];
};
void f() {
Vec<3> v1{{1, 2, 3}};
Vec<3> v2 = {{1, 2, 3}};
}
Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }
If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));
You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}
Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.