Recursive Template Instantiation failed with tuples - c++

I have the following code that iterates through the types of a std::tuple and concatenates their names as strings.
#include <type_traits>
#include <tuple>
#include <string>
template<typename types_T, int n, typename T>
concept tuple_element_is = (std::is_same<typename std::tuple_element<n, types_T>::type, T>::value);
template<typename types_T, int n>
requires tuple_element_is<types_T, n, float>
constexpr const std::string foo() {
if constexpr (n < std::tuple_size<types_T>::value - 1) {
return "float" + foo<types_T, n + 1>();
} else {
return "float";
}
}
template<typename types_T, int n>
requires tuple_element_is<types_T, n, int>
constexpr const std::string foo() {
if constexpr (n < std::tuple_size<types_T>::value - 1) {
return "int" + foo<types_T, n + 1>();
} else {
return "int";
}
}
auto t0 = foo<std::tuple<int>, 0>();
auto t1 = foo<std::tuple<float>, 0>();
auto t2 = foo<std::tuple<int, int>, 0>();
auto t3 = foo<std::tuple<int, float>, 0>();
//auto t4 = foo<std::tuple<float, int>, 0>(); -- does not compile
auto t5 = foo<std::tuple<float, float>, 0>();
auto t6 = foo<std::tuple<int, int, int>, 0>();
auto t7 = foo<std::tuple<int, int, float>, 0>();
//auto t8 = foo<std::tuple<int, float, int>, 0>(); -- does not compile
auto t9 = foo<std::tuple<int, float, float>, 0>();
//auto t10 = foo<std::tuple<float, int, int>, 0>(); -- does not compile
//auto t11 = foo<std::tuple<float, int, float>, 0>(); -- does not compile
//auto t12 = foo<std::tuple<float, float, int>, 0>(); -- does not compile
auto t13 = foo<std::tuple<float, float, float>, 0>();
The instantiation t4, t8, t10, t11, t12 do not compile and produced this error:
note: template argument deduction/substitution failed:
note: constraints not satisfied
In substitution of ‘template<class types_T, int n> requires tuple_element_is<types_T, n, float> constexpr const string foo() [with types_T = std::tuple<float, int>; int n = 1]’:
required from ‘constexpr const string foo() [with types_T = std::tuple<float, int>; int n = 0; std::string = std::__cxx11::basic_string<char>]’
required from here
required for the satisfaction of ‘tuple_element_is<types_T, n, float>’ [with types_T = std::tuple<float, int>; n = 1]
note: the expression ‘std::is_same<typename std::tuple_element<(long unsigned int)n, types_T>::type, T>::value [with n = 1; types_T = std::tuple<float, int>; T = float]’ evaluated to ‘false’
The last note is interesting, because it is true that this expression evaluates to false, but he used the float variant for the second call.
Maybe I'm using it wrong, but the compiler should re-check the requirements for each recursive call of foo and select the correct substitution, in this case int.
More interestingly, it works the other way around for t3, t7
Note that calling foo<std::tuple<float, int>, 1>(); directly also works.
I have tested it with both GCC and clang and they produce the same result.
Any idea?

Clang gives me a very straightforward error message:
error: call to function 'foo' that is neither visible in the template definition nor found by argument-dependent lookup
return "float" + foo<types_T, n + 1>();
^
note: in instantiation of function template specialization 'foo<std::tuple<float, int>, 0>' requested here
auto t4 = foo<std::tuple<float, int>, 0>(); //-- does not compile
^
note: 'foo' should be declared prior to the call site
constexpr const std::string foo() {
The int variant of foo is not visible in the float variant, and "should be declared prior to the call site". In other words, it should be forward-declared, like this:
template<typename types_T, int n>
requires tuple_element_is<types_T, n, int>
constexpr const std::string foo();
Demo
GCC's error message is a little harder to understand, but it's still relatively straightforward when you're used to template related error messages:
error: no matching function for call to 'foo<std::tuple<float, int>, (0 + 1)>()'
12 | return "float" + foo<types_T, n + 1>();
| ~~~~~~~~~~~~~~~~~~~^~
note: candidate: 'template<class types_T, int n> requires tuple_element_is<types_T, n, float> constexpr const std::string foo()'
10 | constexpr const std::string foo() {
It doesn't mention any other candidate, meaning that it only considers the one it can see: the first one. Again, because the second one is not declared by that point.

Related

Variadic constructor fails to initialise an std::array

Here is a snippet from my code:
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
class HexMatrix
{
private:
std::array<Type, NumberOfRows*NumberOfColumns> values;
// ...
public:
template <typename... OtherType>
HexMatrix(OtherType...);
// ...
};
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
template <typename... OtherType>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
}
Until recently, I was able to use this code as is, and initialise the std::array with an std::vector using the constructor above. Then my PC got formatted. Now this doesn't compile anymore, even though I was using and am still using -std=c++2a. Here is the compilation report:
In file included from foo.cpp:3:
LinearAlgebra.hpp: In instantiation of ‘HexMatrix< <template-parameter-1-1>, <anonymous>, <anonymous> >::HexMatrix(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’:
LinearAlgebra.hpp:230:9: required from ‘static HexMatrix<Type, NumberOfRows, NumberOfColumns> HexMatrix< <template-parameter-1-1>, <anonymous>, <anonymous> >::Make(OtherType ...) [with OtherType = {std::vector<long double, std::allocator<long double> >}; Type = long double; unsigned int NumberOfRows = 6; unsigned int NumberOfColumns = 6]’
foo.cpp:93:80: required from here
LinearAlgebra.hpp:299:98: error: no matching function for call to ‘std::array<long double, 36>::array(<brace-enclosed initializer list>)’
299 | HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
| ^
In file included from /usr/include/c++/9/tuple:39,
from /usr/include/c++/9/bits/hashtable_policy.h:34,
from /usr/include/c++/9/bits/hashtable.h:35,
from /usr/include/c++/9/unordered_map:46,
from Using.hpp:5,
from LinearAlgebra.hpp:4,
from foo.cpp:3:
/usr/include/c++/9/array:94:12: note: candidate: ‘std::array<long double, 36>::array()’
94 | struct array
| ^~~~~
/usr/include/c++/9/array:94:12: note: candidate expects 0 arguments, 1 provided
/usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(const std::array<long double, 36>&)’
/usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const std::array<long double, 36>&’
/usr/include/c++/9/array:94:12: note: candidate: ‘constexpr std::array<long double, 36>::array(std::array<long double, 36>&&)’
/usr/include/c++/9/array:94:12: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘std::array<long double, 36>&&’
make: *** [Makefile:16: foo.o] Error 1
What's wrong? I tried to remove the parenthesis, and/or add another pair of brackets around args... but that didn't change anything. Please help?
Reproductible example:
int main(void)
{
const std::vector<long double> foo = { 1.L, 2.L, -6.L, 0.L };
HexMatrix<long double, 2u, 2u> givenMatrix(foo);
return 0;
}
If you want to pass a vector in the constructor, add a new constructor to HexMatrix:
#include <vector>
#include <array>
#include <algorithm>
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
class HexMatrix
{
private:
std::array<Type, NumberOfRows*NumberOfColumns> values;
// ...
public:
template <typename... OtherType>
HexMatrix(OtherType...);
HexMatrix(const std::vector<Type>&);
// ...
};
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
template <typename... OtherType>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(OtherType... args) : values({ args... })
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
}
template <typename Type, unsigned int NumberOfRows, unsigned int NumberOfColumns>
HexMatrix<Type, NumberOfRows, NumberOfColumns>::HexMatrix(const std::vector<Type>& vec)
{
static_assert(NumberOfRows != 0u);
static_assert(NumberOfColumns != 0u);
std::copy_n(vec.begin(), NumberOfRows*NumberOfColumns, values.begin());
}
int main(void)
{
const std::vector<long double> foo = { 1.L, 2.L, -6.L, 0.L };
HexMatrix<long double, 2u, 2u> givenMatrix(foo);
return 0;
}

Check if a tuple dominates another tuple in C++11

I would like a function bool dominates(const std::tuple<T...>& t1, const std::tuple<T...>& t2) which returns true iff tuple t1 dominates tuple t2, i.e. for all i, t1[i] <= t2[i], in contrast with the default <= operator which uses a lexicographic comparison.
I've tried to adapt the answer from this question, but without success. It fails at compilation.
template<typename H>
bool& dominates_impl(bool& b, H&& h1, H&& h2)
{
b &= std::forward<H>(h1) <= std::forward<H>(h2);
return b;
}
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
{
b &= (std::forward<H>(h1) <= std::forward<H>(h2));
return dominates_impl(b, std::forward<T>(t1)..., std::forward<T>(t2)...);
}
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
bool b = true;
int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
(void)ctx;
return b;
}
template <typename ... T>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2)
{
return dominates(t1, t2, gen_indices<sizeof...(T)>{});
}
Compilation errors:
./common.hpp: In instantiation of 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&, integer_sequence<long unsigned int, I ...>) [with T = {long int, long int, long int, long int, long int}; long unsigned int ...I = {0, 1, 2, 3, 4}]':
./common.hpp:107:21: required from 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&) [with T = {long int, long int, long int, long int, long int}]'
examples.cpp:1624:65: required from here
./common.hpp:97:34: error: no matching function for call to 'dominates_impl(bool&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&)'
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:77:7: note: candidate: 'template<class H> bool& dominates_impl(bool&, H&&, H&&)'
77 | bool& dominates_impl(bool& b, H&& h1, H&& h2)
| ^~~~~~~~~~~~~~
./common.hpp:77:7: note: template argument deduction/substitution failed:
./common.hpp:97:34: note: candidate expects 3 arguments, 11 provided
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate: 'bool& dominates_impl(bool&, H&&, H&&, T&& ..., T&& ...) [with H = const long int&; T = {const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&}]'
84 | bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
| ^~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate expects 19 arguments, 11 provided
The problem in you code is in dominates_impl()
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
you can't have two variadic argument list of argument in a function; you can have only one in last position.
But you don't need dominates_impl() at all: you can emulate C++17 template folding writing dominates() (the three argument version) as follows
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
using unused = bool[];
bool b { true };
(void)unused { b, (b = b && std::get<I>(t1) <= std::get<I>(t2))... };
return b;
}
Remember to recover integer_sequence and gen_indices() from the original question.
I was able to make it work in C++11, but only by manually reinventing C++14's std::integer_sequence, and losing constexpr-ability:
#include <tuple>
#include <type_traits>
#include <assert.h>
template<typename T, T ...i> struct integer_sequence {};
template<typename T, T v=0>
struct counter {
static constexpr T n=v;
typedef counter<T, v-1> prev;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl;
template<typename T, T ...i>
struct integer_sequence_impl<T, counter<T>, i...> {
typedef struct integer_sequence<T, 0, i...> t;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl
: integer_sequence_impl<T, typename V::prev, V::n, i...> {};
template<typename T, T n>
using create_integer_sequence=
typename integer_sequence_impl<T, counter<T, n-1>>::t;
template<class T, T N>
using make_integer_sequence=create_integer_sequence<T, N>;
template<typename T1,
typename T2,
std::size_t ...i>
bool dominates_impl(const T1 &t1,
const T2 &t2,
const integer_sequence<std::size_t, i...> &)
{
bool compare[]={
(std::get<i>(t1) <= std::get<i>(t2))...
};
for (auto f:compare)
if (!f)
return false;
return true;
}
template<typename ...T1,
typename ...T2,
typename=typename std::enable_if<sizeof...(T1) == sizeof...(T2)>::type>
bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
make_integer_sequence<std::size_t, sizeof...(T1)>
{});
}
int main()
{
assert(!dominates(std::tuple<int, int>{4, 2},
std::tuple<int, int>{3, 1}));
assert(dominates(std::tuple<int, int>{2, 2},
std::tuple<int, int>{3, 2}));
return 0;
}
A good chunk of the above is a half-baked std::integer_sequence. With that, and C++17's fold expressions this becomes a no-brainer:
#include <tuple>
#include <type_traits>
template<typename T1,
typename T2,
std::size_t ...i>
constexpr bool dominates_impl(const T1 &t1,
const T2 &t2,
const std::integer_sequence<std::size_t, i...> &)
{
return ( (std::get<i>(t1) <= std::get<i>(t2)) && ...);
}
template<typename ...T1,
typename ...T2,
typename=std::enable_if_t<sizeof...(T1) == sizeof...(T2)>>
constexpr bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
std::make_index_sequence<sizeof...(T1)>{});
}
static_assert(!dominates(std::tuple{4, 2},
std::tuple{3, 1}));
static_assert(dominates(std::tuple{2, 2},
std::tuple{3, 2}));

C++17: map type to integer value at compile time

I have the following types:
struct A { };
struct B { };
struct C { };
template <typename Class, uint16_t i>
struct def {
using message_type = Class;
static constexpr uint16_t tag = i;
};
and this tuple:
constexpr auto types = std::make_tuple(def<A, 1>(), def<B, 2>(), def<C, 3>());
Types A, B and C should be mapped to corresponding values (A -> 1 etc.). I want to create something (function, struct) that given object of one of these types will return proper value. I tried doing the following:
template <typename T>
struct gettag {
static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
};
template <typename... Args>
struct tagdb : public gettag<Args>... {
tagdb(std::tuple<Args...> const& t) { }
};
int main() {
tagdb t(types);
A a;
std::cout << t.value(a) << '\n';
}
This does not work, g++ claims that request for member value is ambiguous:
x.cc: In function ‘int main()’:
x.cc:29:17: error: request for member ‘value’ is ambiguous
29 | std::cout << t.value(a) << '\n';
| ^~~~~
x.cc:16:26: note: candidates are: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<C, 3>; decltype (T::tag) = const short unsigned int; typename T::message_type = C]’
16 | static decltype(T::tag) value(typename T::message_type const&) { return T::tag; }
| ^~~~~
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<B, 2>; decltype (T::tag) = const short unsigned int; typename T::message_type = B]’
x.cc:16:26: note: ‘static decltype (T::tag) gettag<T>::value(const typename T::message_type&) [with T = def<A, 1>; decltype (T::tag) = const short unsigned int; typename T::message_type = A]’
I am a little surprised, especially since it clearly shows that each method is parameterized using different types.
Is there a way to make this solution work or should I completely change my approach? Note that what I want to avoid most is writing overloads for each type.
I suggest a solution without a std::tuple and gettag:
struct A { };
struct B { };
struct C { };
template <typename Class, std::uint16_t i>
struct def {
static constexpr std::uint16_t value(Class) {
return i;
}
};
template <typename... Tags>
struct tagdb : public Tags... {
using Tags::value...;
};
template<class... Tags>
constexpr auto make_tagdb(Tags...) {
return tagdb<Tags...>{};
}
// template<class... Tags>
// constexpr auto make_tagdb(std::tuple<Tags...>) {
// return tagdb<Tags...>{};
// }
constexpr auto tags = make_tagdb(def<A, 1>(), def<B, 2>(), def<C, 3>());
int main() {
A a;
std::cout << tags.value(a) << '\n'; // Output: 1
}
The problem is that you have template base classes, all of which declare a member with the same name. The easiest fix is just to pull all the base class value functions into the derived class:
using gettag<Args>::value...;
See https://godbolt.org/z/F_Prhg

Why this template constexpr function doesn't compile on gcc but works well on clang?

As you can see here http://melpon.org/wandbox/permlink/vJSyO14mkbH0MQRq this doesn't compile on gcc with the error:
prog.cc: In instantiation of 'constexpr B convert(A) [with A = unsigned char; B = short unsigned int]':
prog.cc:16:52: required from here
prog.cc:12:1: error: body of constexpr function 'constexpr B convert(A) [with A = unsigned char; B = short unsigned int]' not a return-statement
The code:
#include <stdint.h>
#include <limits>
#include <iostream>
template< typename A, typename B >
constexpr B convert( A a )
{
auto aMax = std::numeric_limits< A >::max();
auto bMax = std::numeric_limits< B >::max();
return a * ( bMax / aMax );
}
int main()
{
std::cout << convert< uint8_t, uint16_t >( 128 ) << std::endl;
return 0;
}
This code requires a C++14 feature called "Relaxing constraints on constexpr-functions". It is supported in Clang since version 3.4, but GCC didn't implement it yet and subsequently complains about your function template.
There is no need for C++14 though, just rewrite it as
template <typename B, typename A> //! Note that I reordered the parameters
constexpr B convert( A a )
{
return a * (std::numeric_limits< B >::max() / std::numeric_limits< A >::max());
}
You can also use alias declarations.
template <typename T, T v>
using iconst = std::integral_constant<T, v>;
template <typename B, typename A>
constexpr B convert( A a )
{
using aMax = iconst<A, std::numeric_limits< A >::max()>;
using bMax = iconst<B, std::numeric_limits< B >::max()>;
return a * (bMax::value / aMax::value);
}
Demo

Is this a compiler bug or a programmer bug?

I'm playing with tuples as compile time lists. In How can I have multiple parameter packs in a variadic template? I answered myself with some code that works in both GCC and Clang, but Clang wont compile now that I've added (what I think is) perfect forwarding. It complains that As... and as... have different lengths in std::forward<As>(as).... How can this be true when As... is the type of as...? It's As&&... as in the parameters.
#include <iostream>
#include <tuple>
template < typename ... >
struct two_impl {};
// Base case
template < typename F,
typename ...Bs >
struct two_impl < F, std::tuple <>, std::tuple< Bs... > > {
void operator()(F&& f, Bs&&... bs) {
f(std::forward<Bs>(bs)...);
}
};
// Recursive case
template < typename F,
typename A,
typename ...As,
typename ...Bs >
struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> > {
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs) {
auto impl = two_impl < F, std::tuple < As&&... >, std::tuple < Bs&&..., A&& > >();
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
}
};
template < typename F, typename ...Ts >
void two(F&& f, Ts&& ...ts) {
auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >();
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
}
struct Test {
void operator()(int i, float f, double d) {
std::cout << i << std::endl << f << std::endl << d << std::endl;
}
};
int main () {
two(Test(), 1, 1.5f, 2.1);
}
Compiling with clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
clang -lstdc++ -std=c++0x multiple_parameter_packs.cpp
multiple_parameter_packs.cpp:24:50: error: pack expansion contains parameter packs 'As' and 'as' that have different
lengths (1 vs. 2)
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
~~ ~~ ^
multiple_parameter_packs.cpp:24:5: note: in instantiation of member function 'two_impl<Test, std::tuple<float &&,
double &&>, std::tuple<int &&> >::operator()' requested here
impl(std::forward<F>(f), std::forward<As>(as)..., std::forward<Bs>(bs)..., std::forward<A>(a));
^
multiple_parameter_packs.cpp:31:3: note: in instantiation of member function 'two_impl<Test, std::tuple<int, float,
double>, std::tuple<> >::operator()' requested here
impl(std::forward<F>(f), std::forward<Ts>(ts)...);
^
multiple_parameter_packs.cpp:41:3: note: in instantiation of function template specialization
'two<Test, int, float, double>' requested here
two(Test(), 1, 1.5f, 2.1);
^
1 error generated.
Compilation exited abnormally with code 1 at Fri Mar 23 14:25:14
This appears to be a bug in an old version of Clang. The code works fine with trunk Clang, with either libstdc++ or libc++.
$ clang++ multiple_parameter_packs.cpp -std=c++11 -stdlib=libc++
$ ./a.out
1
1.5
2.1
I don't think that this:
void operator()(F&& f, A&& a, As&&... as, Bs&&... bs)
is quite possible.
A parameter pack should be the last argument, and As&&... as is followed by another pack here.