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.
Use a
std::index_sequence
to chew through the tupleUse a specialization to pick up either a
std::tuple<>
or astd::tuple<T>
out of the original tuple, for each indexed element.Use
std::tuple_cat
to glue everything together.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 asstd::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
Why Do Some People Prefer "T Const&" Over "Const T&"
How to Fix: /Usr/Lib/Libstdc++.So.6: Version 'Glibcxx_3.4.15' Not Found
C++11 Is_Same Type Trait for Templates
Gcc Error: Explicit Specialization in Non-Namespace Scope
Sqlite3_Exec() Callback Function Clarification
Gsl::Not_Null<T*> VS. Std::Reference_Wrapper<T> VS. T&
Dereferencing a Pointer When Passing by Reference
Check the File-Size Without Opening File in C++
Can a Declaration Affect the Std Namespace
What's the Usual Way of Controlling Frame Rate
Memory-Efficient C++ Strings (Interning, Ropes, Copy-On-Write, etc)
C++ Push Multiple Types Onto Vector
Findchessboardcorners Cannot Detect Chessboard on Very Large Images by Long Focal Length Lens
Why Use ++I Instead of I++ in Cases Where the Value Is Not Used Anywhere Else in the Statement
Columns Auto-Resize to Size of Qtableview
Reverse String C++ Using Char Array
One Way of Eliminating C4251 Warning When Using Stl-Classes in the Dll-Interface
Differencebetween Std::Quick_Exit and Std::Abort and Why Was Std::Quick_Exit Needed