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}));
Related
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;
}
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.
I have following code:
enum class type
{
zero,
one,
two
};
std::ostream& operator<<(std::ostream &os, type const &t)
{
switch(t)
{
case type::zero: os << "zero"; break;
case type::one: os << "one"; break;
case type::two: os << "two"; break;
default: os.setstate(std::ios_base::failbit);
}
return os;
}
template <typename K>
using pv = std::pair<type, std::vector<K>>;
template <typename K>
using ps = std::pair<type, K>;
template <typename B, typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
std::cout << std::endl;
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
rec<B>(j, f, t...);
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
auto [n, v] = t0;
for(auto k : v) {
rec<B>(j, f, s0, t..., ps<K>{n, k});
}
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
auto [n, v] = t0;
for(auto k : v) {
rec<B>(j, f, t..., ps<K>{n, k});
}
}
void pro(int j, type t, std::string const &s)
{
std::cout << j << ":" << t << ":" << s << ", ";
}
int main()
{
std::vector<std::string> v0{"V00", "V01"};
std::vector<std::string> v1{"V10", "V11"};
std::vector<std::string> v2{"V20", "V21"};
int j = 0;
rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
return 0;
}
This compiles fine with clang++ (clang version 6.0.0-1ubuntu2) but fails with g++ (g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0). GCC throws the following error:
main.cpp: In function ‘int main()’:
main.cpp: error: call of overloaded ‘rec<char>(int&, void (&)(int, type, const string&), std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >)’ is ambiguous
rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
^
main.cpp: note: candidate: void rec(int, F&, ps<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::vector<std::__cxx11::basic_string<char> >; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; ps<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
^~~
main.cpp: note: candidate: void rec(int, F&, pv<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::__cxx11::basic_string<char>; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; pv<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
^~~
Why are the functions at comment 1 and comment 3 ambiguous even though the parameter types pv and ps are different?
Interestingly, the following code works with both clang++ and g++ (note the removal of template parameter B from functions rec):
enum class type
{
zero,
one,
two
};
std::ostream& operator<<(std::ostream &os, type const &t)
{
switch(t)
{
case type::zero: os << "zero"; break;
case type::one: os << "one"; break;
case type::two: os << "two"; break;
default: os.setstate(std::ios_base::failbit);
}
return os;
}
template <typename K>
using pv = std::pair<type, std::vector<K>>;
template <typename K>
using ps = std::pair<type, K>;
template <typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
std::cout << std::endl;
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
rec(j, f, t...);
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
auto [n, v] = t0;
for(auto k : v) {
rec(j, f, s0, t..., ps<K>{n, k});
}
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
auto [n, v] = t0;
for(auto k : v) {
rec(j, f, t..., ps<K>{n, k});
}
}
void pro(int j, type t, std::string const &s)
{
std::cout << j << ":" << t << ":" << s << ", ";
}
int main()
{
std::vector<std::string> v0{"V00", "V01"};
std::vector<std::string> v1{"V10", "V11"};
std::vector<std::string> v2{"V20", "V21"};
int j = 0;
rec(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
return 0;
}
Why does the second code work with g++ but not the first?
The following code compiles:
#include "List.h"
//namespace prototypeInd{namespace templates{ //Uncomment for error
template <size_t N, typename Lambda>
inline void static_for(const Lambda& f,std::integral_constant<size_t,N>) {
static_for(f, std::integral_constant<size_t,N-1>());
f(std::integral_constant<size_t,N-1>());
}
template <typename Lambda>
inline void static_for(const Lambda& f, std::integral_constant<size_t,1>) {
f(std::integral_constant<size_t,0>());
}
//}} //Uncomment for error
//using namespace prototypeInd::templates; //Uncomment for error
template<size_t N>
using ic = std::integral_constant<size_t,N>;
typedef List<ic<0>,ic<1>,ic<2>,ic<3>,ic<4>,ic<5>,ic<6>,ic<7> > list;
int main(int argc,char** args){
static_for([](auto i){ std::cout << list::IndexOf<i>() << std::endl; },list::SizeOf());
}
But as soon as you uncomment the marked lines it gives a huge whopper of an error.
Here is List.h:
template<class First, class... Rest>
struct SizeOf_ : std::integral_constant<size_t, SizeOf_<Rest...>() + 1>{};
template<class First>
struct SizeOf_<First> : std::integral_constant<size_t,1> { };
template<size_t index, class First, class... Rest>
struct Index_{
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
typedef typename Index_<index - 1, Rest...>::value value;
};
template<class First, class... Rest>
struct Index_<0,First, Rest...>{
typedef First value;
};
template<class First_t, class... Rest_t>
struct List{
template<size_t i>
using IndexOf = typename Index_<i,First_t,Rest_t...>::value;
typedef SizeOf_<First_t, Rest_t...> SizeOf;
};
This seems inexplicable....A namespace should not change the function of the code?!?!?!? (or should it?).
I am using g++ with std=c++14....Any help is appreciated
Why does having a namespace change anything?
When Alf compiles this code with g++ 5.1.0 and removes the out-commenting, that compiler reports as first error that
foo.cpp:11:5: error: static assertion failed: Index is outside of bounds
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
The complete diagnostics avalanche for that first error:
foo.cpp: In instantiation of 'struct Index_<18446744073709551615ull, std::integral_constant<long long unsigned int, 0ull>, std::integral_constant<long long unsigned int, 1ull>, std::integral_constant<long long unsigned int, 2ull>, std::integral_constant<long long unsigned int, 3ull>, std::integral_constant<long long unsigned int, 4ull>, std::integral_constant<long long unsigned int, 5ull>, std::integral_constant<long long unsigned int, 6ull>, std::integral_constant<long long unsigned int, 7ull> >':
foo.cpp:21:64: required by substitution of 'template<class First_t, class ... Rest_t> template<long long unsigned int i> using IndexOf = typename Index_<i, First_t, Rest_t ...>::value [with long long unsigned int i = i; First_t = std::integral_constant<long long unsigned int, 0ull>; Rest_t = {std::integral_constant<long long unsigned int, 1ull>, std::integral_constant<long long unsigned int, 2ull>, std::integral_constant<long long unsigned int, 3ull>, std::integral_constant<long long unsigned int, 4ull>, std::integral_constant<long long unsigned int, 5ull>, std::integral_constant<long long unsigned int, 6ull>, std::integral_constant<long long unsigned int, 7ull>}]'
foo.cpp:52:38: required from 'main(int, char**):: [with auto:1 = std::integral_constant<long long unsigned int, 18446744073709551615ull>]'
foo.cpp:33:15: recursively required from 'void prototypeInd::templates::static_for(const Lambda&, std::integral_constant<long long unsigned int, N>) [with long long unsigned int N = 7ull; Lambda = main(int, char**)::]'
foo.cpp:33:15: required from 'void prototypeInd::templates::static_for(const Lambda&, std::integral_constant<long long unsigned int, N>) [with long long unsigned int N = 8ull; Lambda = main(int, char**)::]'
foo.cpp:52:90: required from here
foo.cpp:11:5: error: static assertion failed: Index is outside of bounds
static_assert(index < SizeOf_<First,Rest...>(), "Index is outside of bounds");
^
I am trying to map collection of values of different types into continuous space. To do that (efficiently) I need to know total size of all elements and offset of each of them. Getting total size if collection is given as a Tuple is easy. It also allows to pre-calculate offsets. I struggle with obtaining offset by type. To simplify example I am assuming that types within Tuple are going to be unique (in reality Tuple are going to have pairs of unique tag + non-unique value). Here is my current not-compiling attempt:
#include <cstddef>
#include <iostream>
#include <tuple>
struct A
{
std::size_t size()
{
return 3;
}
};
struct B
{
std::size_t size()
{
return 2;
}
};
struct C
{
std::size_t size()
{
return 4;
}
};
template <typename Tuple>
struct Foo
{
const Tuple& tuple_;
std::array<int, std::tuple_size<Tuple>::value> array_;
Foo(const Tuple& tuple) : tuple_(tuple)
{
std::cout << init() << '\n';
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <std::size_t INDEX = 0>
typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
init()
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <std::size_t INDEX = 0>
typename std::enable_if<std::tuple_size<Tuple>::value != INDEX, std::size_t>::type
init()
{
auto offset = init<INDEX + 1>();
std::cout << "index: "<< INDEX << "; offset: " << offset << '\n';
array_[INDEX] = offset;
return offset + std::get<INDEX>(tuple_).size();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <std::size_t INDEX = 0, typename T>
typename std::enable_if<std::tuple_size<Tuple>::value == INDEX, std::size_t>::type
offset(const T&)
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <std::size_t INDEX = 0, typename T>
typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && !std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
offset(const T& t)
{
return offset<INDEX + 1>(t);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template <std::size_t INDEX = 0, typename T>
typename std::enable_if<(std::tuple_size<Tuple>::value != INDEX) && std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value, std::size_t>::type
offset(const T&)
{
return array_[INDEX];
}
};
int main()
{
A a;
B b;
C c;
auto t = std::tie(a, b, c);
using T = decltype(t);
Foo<T> foo(t);
std::cout << foo.offset(a) << '\n';
//std::cout << foo.offset(b) << '\n';
//std::cout << foo.offset(c) << '\n';
}
compiler error:
In file included from prog.cpp:3:0:
/usr/include/c++/4.8/tuple: In instantiation of ‘struct std::tuple_element<1u, std::tuple<C&> >’:
/usr/include/c++/4.8/tuple:680:12: recursively required from ‘struct std::tuple_element<2u, std::tuple<B&, C&> >’
/usr/include/c++/4.8/tuple:680:12: required from ‘struct std::tuple_element<3u, std::tuple<A&, B&, C&> >’
prog.cpp:79:22: recursively required from ‘typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 1u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:79:22: required from ‘typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type Foo<Tuple>::offset(const T&) [with unsigned int INDEX = 0u; T = A; Tuple = std::tuple<A&, B&, C&>; typename std::enable_if<((std::tuple_size<_Tp>::value != INDEX) && (! std::is_same<typename std::tuple_element<INDEX, Tuple>::type, T>::value)), unsigned int>::type = unsigned int]’
prog.cpp:101:27: required from here
/usr/include/c++/4.8/tuple:680:12: error: invalid use of incomplete type ‘struct std::tuple_element<0u, std::tuple<> >’
struct tuple_element<__i, tuple<_Head, _Tail...> >
^
In file included from /usr/include/c++/4.8/tuple:38:0,
from prog.cpp:3:
/usr/include/c++/4.8/utility:84:11: error: declaration of ‘struct std::tuple_element<0u, std::tuple<> >’
class tuple_element;
^
Your code is correct except for one issue.
The problem is that you construct a std::tuple using the std::tie() helper function, which results in a tuple of references - std::tuple<A&, B&, C&>.
On the other hand, you have offset(const T& t) where the deduced type T can be just A, B, or C (that is, the reference is not part of the deduced type, only of the parameter's type). That said, your condition is_same<tuple_element<...>::type, T> always fails because one type is a reference, whereas the other is not, e.g. std::is_same<A&, A>. To solve this, you should first decay the type (or at least remove a reference) before it is compared to another one for equality.
Having said that, your every condition should look as follows:
std::is_same<typename std::decay<typename std::tuple_element<INDEX, Tuple>::type>::type, T>::value
// ~~~~~~~~~~~~~~~~~~~^ ~~~~~~^
or in c++14:
std::is_same<std::decay_t<typename std::tuple_element<INDEX, Tuple>::type>::type>, T>::value
// ~~~~~~~~~~~~^ ^
DEMO
It seems that you want something like:
template <typename T, typename Tuple> struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::tuple<T, Ts...>> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_index<T, std::tuple<Tail, Ts...>> :
std::integral_constant<std::size_t, 1 + get_index<T, std::tuple<Ts...>>::value> {};
And then replace all your offset method by
template <typename T>
std::size_t
offset(const T&)
{
return array_[get_index<T&, Tuple>::value];
}
Live example.
Notice than in your example Tuple is std::tuple<A&, B&, C&> and you test for equality with A...