Get Part of Std::Tuple

get part of std::tuple

With help of a compile-time integer list:

#include <cstdlib>

template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};

template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
};

template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};

We could construct the tail simply by parameter-pack expansion:

#include <tuple>

template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
return std::make_tuple(std::get<indices>(tpl)...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}

template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
// this means:
// tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}

Usage:

#include <cstdio>

int main()
{
auto a = std::make_tuple(1, "hello", 7.9);
auto b = tuple_tail(a);

const char* s = nullptr;
double d = 0.0;
std::tie(s, d) = b;
printf("%s %g\n", s, d);
// prints: hello 7.9

return 0;
}

(On ideone: http://ideone.com/Tzv7v; the code works in g++ 4.5 to 4.7 and clang++ 3.0)

How to extract all tuple elements of given type(s) into new tuple

Only C++17 is needed here.

std::tuple_cat is one of my favorite tools.

  1. Use a std::index_sequence to chew through the tuple

  2. Use a specialization to pick up either a std::tuple<> or a std::tuple<T> out of the original tuple, for each indexed element.

  3. Use std::tuple_cat to glue everything together.

  4. The only tricky part is checking if each tuple element is wanted. To do that, put all the wanted types into its own std::tuple, and use a helper class for that part, too.

#include <utility>
#include <tuple>
#include <iostream>

// Answer one simple question: here's a type, and a tuple. Tell me
// if the type is one of the tuples types. If so, I want it.

template<typename wanted_type, typename T> struct is_wanted_type;

template<typename wanted_type, typename ...Types>
struct is_wanted_type<wanted_type, std::tuple<Types...>> {

static constexpr bool wanted=(std::is_same_v<wanted_type, Types>
|| ...);
};

// Ok, the ith index in the tuple, here's its std::tuple_element type.
// And wanted_element_t is a tuple of all types we want to extract.
//
// Based on which way the wind blows we'll produce either a std::tuple<>
// or a std::tuple<tuple_element_t>.

template<size_t i, typename tuple_element_t,
typename wanted_element_t,
bool wanted=is_wanted_type<tuple_element_t, wanted_element_t>::wanted>
struct extract_type {

template<typename tuple_type>
static auto do_extract_type(const tuple_type &t)
{
return std::tuple<>{};
}
};

template<size_t i, typename tuple_element_t, typename wanted_element_t>
struct extract_type<i, tuple_element_t, wanted_element_t, true> {

template<typename tuple_type>
static auto do_extract_type(const tuple_type &t)
{
return std::tuple<tuple_element_t>{std::get<i>(t)};
}
};

// And now, a simple fold expression to pull out all wanted types
// and tuple-cat them together.

template<typename wanted_element_t, typename tuple_type, size_t ...i>
auto get_type_t(const tuple_type &t, std::index_sequence<i...>)
{
return std::tuple_cat( extract_type<i,
typename std::tuple_element<i, tuple_type>::type,
wanted_element_t>::do_extract_type(t)... );
}

template<typename ...wanted_element_t, typename ...types>
auto get_type(const std::tuple<types...> &t)
{
return get_type_t<std::tuple<wanted_element_t...>>(
t, std::make_index_sequence<sizeof...(types)>());
}

int main()
{
std::tuple<int, const char *, double> t{1, "alpha", 2.5};

std::tuple<double, int> u=get_type<int, double>(t);

std::cout << std::get<0>(u) << " " << std::get<1>(u) << std::endl;

std::tuple<int, int, int, char, char, char, double, double, float> tt;

auto uu=get_type<float, double>(tt);

static_assert(std::is_same_v<decltype(uu),
std::tuple<double, double, float>>);

return 0;
}

How to get reference to an element of a std::tuple?

std::get returns a reference(either const or non-const), so this works:

void fun(int &a) {
a = 15;
}

void test() {
std::tuple<int, char> foo{ 12, 'a' };
fun(std::get<0>(foo));
}

Demo here.

How to obtain a part of a tuple?

UPDATE

If you have got here seeking code that will extract a specified
subsequence of a given std::tuple and/or define the type of a subsequence
of a given std::tuple-type
, skip to Anwser to the apparent question.
The OP's question is actually not that one.

Answer to the actual question

This will do:

#include <cstddef>
#include <type_traits>
#include <tuple>

template<size_t I, typename TupleType, typename Enable = void>
struct first_elements;

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I == 0)>::type>
{
using type = typename std::tuple_element<0,TupleType>::type;
};

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I == 1)>::type>
{
using type =
typename std::tuple<typename std::tuple_element<0,TupleType>::type>;
};

template<size_t I, typename TupleType>
struct first_elements<I,TupleType,typename std::enable_if<(I > 1)>::type>
{
using next =
typename std::tuple<typename std::tuple_element<I - 1,TupleType>::type>;
using prev = typename first_elements<I - 1,TupleType>::type;
using type = decltype(
std::tuple_cat(std::declval<prev>(),std::declval<next>())
);
};

// A test program...

#include <iostream>

using namespace std;

int main(int argc, char **argv)
{
auto tup = std::tuple<char,int,float,double>();
cout <<
std::is_same<first_elements<0,decltype(tup)>::type,char>::value << endl;
cout <<
std::is_same<first_elements<1,decltype(tup)>::type,std::tuple<char>>::value
<< endl;
cout <<
std::is_same<
first_elements<2,decltype(tup)>::type,
std::tuple<char,int>>::value
<< endl;
cout <<
std::is_same<
first_elements<3,decltype(tup)>::type,
std::tuple<char,int,float>>::value
<< endl;
cout <<
std::is_same<
first_elements<4,decltype(tup)>::type,
std::tuple<char,int,float,double>>::value
<< endl;
return 0;
}

No special precautions for compiletime range-errors. They will barf as
std::tuple makes them barf.

(gcc 4.7.2/4.8.1.clang 3.3, -std=c++11)

Anwser to the apparent question

#include <type_traits>
#include <tuple>

/* A class template to obtain the type and value of the
the subsequence [First,Last) of a tuple type TupleType

First:- The start of the subsequence [First,Last)
Last:- The end of the subsequence [First,Last)
TupleType: - The std::tuple type to be queried.
Enable:- SFINAE parameter

The public member `type` is defined as the type of the
subsequence [First,Last) of `TupleType`.

The static member function:

`type get(TupleType const & tup)`

returns the `std::tuple` of the subsequence [First,Last) of `tup`.
`std::tuple<>` is returned when `First`
is out of range. The terminal sub-tuple indexed by `First` is
returned if only `Last` is out of range.

*/
template<
unsigned First, unsigned Last, typename TupleType, typename Enable = void
>
struct tuple_part;

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
First,Last,TupleType,
typename std::enable_if<
(First >= Last || First >= std::tuple_size<TupleType>::value)
>::type
>
{
using type = std::tuple<>;

static constexpr type get(TupleType const & tup) {
return type();
}
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
First,Last,TupleType,
typename std::enable_if<
(Last == First + 1 && First < std::tuple_size<TupleType>::value)
>::type
>
{
using type =
typename std::tuple<typename std::tuple_element<First,TupleType>::type>;

static constexpr type get(TupleType const & tup) {
return type(std::get<First>(tup));
}
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
First,Last,TupleType,
typename std::enable_if<
(Last > First + 1 && Last <= std::tuple_size<TupleType>::value)
>::type
>
{
using head = typename tuple_part<First,First + 1,TupleType>::type;
using tail = typename tuple_part<First + 1,Last,TupleType>::type;
using type = decltype(
std::tuple_cat(std::declval<head>(),std::declval<tail>())
);

static constexpr type get(TupleType const & tup) {
return std::tuple_cat(
tuple_part<First,First + 1,TupleType>::get(tup),
tuple_part<First + 1,Last,TupleType>::get(tup)
);
}
};

template<unsigned First, unsigned Last, typename TupleType>
struct tuple_part<
First,Last,TupleType,
typename std::enable_if<
(Last > First + 1 && Last > std::tuple_size<TupleType>::value)
>::type
> : tuple_part<First,std::tuple_size<TupleType>::value,TupleType>
{
using base_type =
tuple_part<First,std::tuple_size<TupleType>::value,TupleType>;
using type = typename base_type::type;
};

/*
`get_part<First,Last>(TupleType const & tup)`
returns the `std::tuple` of the subsequence [First,Last) of `tup`
*/
template<unsigned First, unsigned Last, typename TupleType>
constexpr
decltype(
tuple_part<First,Last,TupleType>::get(std::declval<TupleType>())
)
get_part(TupleType const & tup)
{
return tuple_part<First,Last,TupleType>::get(tup);
}

/*
`get_part<First>(TupleType const & tup)`
returns the `std::tuple` of the terminal subsequence of `tup`
indexed by `First`
*/
template<unsigned First, typename TupleType>
constexpr
decltype(
get_part<First,std::tuple_size<TupleType>::value>(std::declval<TupleType>())
)
get_part(TupleType const & tup)
{
return get_part<First,std::tuple_size<TupleType>::value>(tup);
}

// A test program...

#include <cassert>

int main(int argc, char **argv)
{
using type = std::tuple<char,int,float,double>;
constexpr type t0(1,2,3.0,4.0);
constexpr auto p0 = get_part<0,1>(t0);
assert(p0 == std::tuple<char>(1));
auto p1 = get_part<0,2>(t0);
assert((p1 == std::tuple<char,int>(1,2)));
auto p2 = get_part<0,3>(t0);
assert((p2 == std::tuple<char,int,float>(1,2,3.0)));
auto p3 = get_part<0>(t0);
assert((p3 == std::tuple<char,int,float,double>(1,2,3.0,4.0)));
auto p4 = get_part<1,2>(t0);
assert(p4 == std::tuple<int>(2));
auto p5 = get_part<1,3>(t0);
assert((p5 == std::tuple<int,float>(2,3.0)));
auto p6 = get_part<1>(t0);
assert((p6 == std::tuple<int,float,double>(2,3.0,4.0)));
auto p7 = get_part<2,3>(t0);
assert(p7 == std::tuple<float>(3.0));
auto p8 = get_part<2,4>(t0);
assert((p8 == std::tuple<float,double>(3.0,4.0)));
auto p9 = get_part<3>(t0);
assert(p9 == std::tuple<double>(4.0));
auto p10 = get_part<3,5>(t0);
assert(p10 == std::tuple<double>(4.0));
auto p11 = get_part<4,4>(t0);
assert(p11 == std::tuple<>());
auto p12 = get_part<5,4>(t0);
assert(p12 == std::tuple<>());
return 0;
}

// EOF

(gcc 4.7.2/4.8.1,clang 3.2/3.3, -std=c++11)

Accessing elements in a vector of tuples C++

I would change to a range-based for loop

for (auto const& edge : edges) {
std::cout << std::get<0>(edge) << " "<< std::get<1>(edge)<< " ";
}

Otherwise to access each edge, you need to dereference your iterator using * to get the actual tuple itself

for (auto iter = std::begin(edges); iter != std::end(edges); ++iter) {
std::cout << std::get<0>(*iter) << " "<< std::get<1>(*iter)<< " ";
}

Get first element of std::tuple satisfying trait

Here's a surprisingly simple way without using recursion:

template <template <typename...> typename T, typename... Ts>
constexpr int index_of_integral(const T<Ts...>&)
{
const bool a[] = { std::is_integral_v<Ts>... };
for (int i = 0; i < sizeof...(Ts); ++i) if (a[i]) return i;
return -1;
}

template <typename T>
constexpr decltype(auto) get_if_integral(T&& t)
{
return std::get<index_of_integral(t)>(std::forward<T>(t));
}

int main()
{
constexpr auto t = std::make_tuple(3.14, 42, "xyzzy");
static_assert(get_if_integral(t) == 42);
}

It could easily be extended to be parametrized on the trait.

The only things that make it C++17 are the is_integral_v variable template and the single-argument static_assert. Everything else is C++14.

Note that in C++20 the for loop could be replaced with std::find and std::distance.

Ideally it should throw an exception instead of returning -1, but compilers don't seem to like that.

Inspired by this answer.

Calling a common method of tuple elements

There may be snazzier C++17 ways of doing it, but there is always good old-fashioned partially-specialized recursion. We'll make a struct that represents your recursive algorithm, and then we'll build a function wrapper around that struct to aid in type inference. First, we'll need some imports.

#include <tuple>
#include <utility>
#include <iostream> // Just for debugging later :)

Here's our structure definition.

template <typename Input, typename... Ts>
struct ApplyOp;

Not very interesting. It's an incomplete type, but we're going to provide specializations. As with any recursion, we need a base case and a recursive step. We're inducting on the tuple elements (you're right to think of this as a fold-like operation), so our base case is when the tuple is empty.

template <typename Input>
struct ApplyOp<Input> {
Input apply(Input x) {
return x;
}
};

In this case, we just return x. Computation complete.

Now our recursive step takes a variable number of arguments (at least one) and invokes .apply.

template <typename Input, typename T, typename... Ts>
struct ApplyOp<Input, T, Ts...> {
auto apply(Input x, const T& first, const Ts&... rest) {
auto tail_op = ApplyOp<Input, Ts...>();
return first.apply(tail_op.apply(x, rest...));
}
};

The tail_op is our recursive call. It instantiates the next version of ApplyOp. There are two apply calls in this code. first.apply is the apply method in the type T; this is the method you control which determines what happens at each step. The tail_op.apply is our recursive call to either another version of this apply function or to the base case, depending on what Ts... is.

Note that we haven't said anything about tuples yet. We've just taken a variadic parameter pack. We're going to convert the tuple into a parameter pack using an std::integer_sequence (More specifically, an std::index_sequence). Basically, we want to take a tuple containing N elements and convert it to a sequence of parameters of the form

std::get<0>(tup), std::get<1>(tup), ..., std::get<N-1>(tup)

So we need to get an index sequence from 0 up to N-1 inclusive (where N-1 is our std::tuple_size).

template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
// ???
}

That complicated-looking type alias is building our index sequence. We take the tuple's size (std::tuple_size<std::tuple<Ts...>>::value) and pass it to std::make_index_sequence, which gives us an std::index_sequence<0, 1, 2, ..., N-1>. Now we need to get that index sequence as a parameter pack. We can do that with one extra layer of indirection to get type inference.

template <typename Input, typename... Ts, std::size_t... Is>
auto apply(const std::tuple<Ts...>& tpl, Input x, std::index_sequence<Is...>) {
auto op = ApplyOp<Input, Ts...>();
return op.apply(x, std::get<Is>(tpl)...);
}

template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
return apply(tpl, x, seq());
}

The second apply is the one outside users call. They pass a tuple and an input value. Then we construct an std::index_sequence of the appropriate type and pass that to the first apply, which uses that index sequence to access each element of the tuple in turn.

Complete, runnable example



Related Topics



Leave a reply



Submit