defining a variadic coordinate (tuple) type in C++17?
Asked Answered
C

4

5

I wanted to define a variadic tuple type to represent coordinates. For example, for some magic type:

template <unsigned int N>
struct CoordT {
  typedef std::tuple<_some_magic_> coord_type;
};

I'd like to have CoordT<3>::coord_type to be the 3-dimensional coordinate type:

std::tuple<double, double, double>

.

But I don't know how to use template programming to generate N repeated doubles.

Can anyone please help explain how to write it?

Caponize answered 9/1, 2019 at 0:11 Comment(1)
@Justin Good point. Thanks.Caponize
S
4

Use std::make_integer_sequence to generate a pack of the appropriate length, then map the elements to doubles:

template <size_t n>
struct TupleOfDoubles {
    template <size_t... i>
    static auto foo(std::index_sequence<i...>) {
        return std::make_tuple(double(i)...);
    }
    using type = decltype(foo(std::make_index_sequence<n>{}));
};

http://coliru.stacked-crooked.com/a/7950876813128c55

Shantae answered 9/1, 2019 at 0:16 Comment(2)
Thanks! Could you add an example on how to use the foo function to create a tuple of doubles?Caponize
@tinlyx check the coliru linkShantae
J
2

If you don't literally need a std::tuple, but just need something which acts like a tuple, use std::array:

template <unsigned int N>
struct CoordT {
  typedef std::array<double, N> coord_type;
};

std::array has overloads for std::get<I>, std::tuple_size, and std::tuple_element. Most library and language facilities which accept a tuple-like element will support std::array, such as std::apply and structured bindings.

Jinajingle answered 22/1, 2019 at 18:13 Comment(0)
K
0

Is too late to play?

If for you is acceptable to declare (no definition is needed) a variadic template function as follows

template <std::size_t ... Is>
constexpr auto toIndexSeq (std::index_sequence<Is...> a)
   -> decltype(a);

and that the coord_type is defined in a CoordT specialization, you can write it as follows

template <std::size_t N,
          typename = decltype(toIndexSeq(std::make_index_sequence<N>{}))>
struct CoordT;

template <std::size_t N, std::size_t ... Is>
struct CoordT<N, std::index_sequence<Is...>>
 { using coord_type = std::tuple<decltype((void)Is, 0.0)...>; };

The following is a full C++14 compiling example

#include <tuple> 
#include <type_traits> 

template <std::size_t ... Is>
constexpr auto toIndexSeq (std::index_sequence<Is...> a)
   -> decltype(a);

template <std::size_t N,
          typename = decltype(toIndexSeq(std::make_index_sequence<N>{}))>
struct CoordT;

template <std::size_t N, std::size_t ... Is>
struct CoordT<N, std::index_sequence<Is...>>
 { using coord_type = std::tuple<decltype((void)Is, 0.0)...>; };


int main()
 {
   using t0 = std::tuple<double, double, double, double>;
   using t1 = typename CoordT<4u>::coord_type;

   static_assert( std::is_same<t0, t1>::value, "!" );
 }
Kept answered 9/1, 2019 at 3:10 Comment(0)
J
0

A very concise way to do this is by using std::tuple_cat and std::array:

template <unsigned int N>
struct CoordT {
  using coord_type = decltype(std::tuple_cat(std::array<double, N>{}));
};

std::tuple_cat is allowed to support tuple-like types such as std::array, but not guaranteed. However, every implementation I checked supports this.

Jinajingle answered 22/1, 2019 at 18:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.