TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
tuple_tools.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2013-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2015 Igor Krivenko
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You may obtain a copy of the License at
17// https://www.gnu.org/licenses/gpl-3.0.txt
18//
19// Authors: Igor Krivenko, Olivier Parcollet, Nils Wentzell
20
25
26#pragma once
27
28#include "./macros.hpp"
29
30#include <cstddef>
31#include <ostream>
32#include <tuple>
33#include <type_traits>
34#include <utility>
35
36namespace triqs {
37
38 // Lazy adaptor for zipping tuples together.
39 template <typename... T> struct _triqs_zipped_tuple {
40 std::tuple<T...> _tu;
41 template <typename... U> _triqs_zipped_tuple(U &&...u) : _tu(std::forward<U>(u)...) {}
42
43 // Get a tuple containing the I-th element of each tuple.
44 template <size_t I, size_t... Is> auto _get(std::index_sequence<Is...>) { return std::tie(std::get<I>(std::get<Is>(_tu))...); }
45 template <size_t I, size_t... Is> [[nodiscard]] auto _get(std::index_sequence<Is...>) const {
46 return std::tie(std::get<I>(std::get<Is>(_tu))...);
47 }
48 };
49
50 // Build a zipped tuple of tuples.
51 template <typename... T> _triqs_zipped_tuple<T...> zip_tuples(T &&...x) { return {std::forward<T>(x)...}; }
52
53} // namespace triqs
54
55// Specialize std::get / std::tuple_size for _triqs_zipped_tuple and add a reversed lazy tuple to std.
56namespace std {
57
58 // Specialization of std::tuple_size for zipped tuples.
59 template <typename T0, typename... T> class tuple_size<triqs::_triqs_zipped_tuple<T0, T...>> : public std::tuple_size<std::decay_t<T0>> {};
60
61 // Specialization of std::get for zipped tuples.
62 template <size_t I, typename... T> decltype(auto) get(triqs::_triqs_zipped_tuple<T...> const &tu) {
63 return tu.template _get<I>(std::make_index_sequence<sizeof...(T)>());
64 }
65
66 // Lazy adaptor for reversing a tuple.
67 template <typename TU> struct _triqs_reversed_tuple {
68 TU _x;
69 };
70
71 // Build a reversed view of a tuple.
72 template <typename... T> _triqs_reversed_tuple<std::tuple<T...>> reverse(std::tuple<T...> &&x) { return {std::move(x)}; }
73 template <typename... T> _triqs_reversed_tuple<std::tuple<T...> &> reverse(std::tuple<T...> &x) { return {x}; }
74 template <typename... T> _triqs_reversed_tuple<std::tuple<T...> const &> reverse(std::tuple<T...> const &x) { return {x}; }
75
76 // Specializations of std::get for reversed tuples.
77 template <int pos, typename TU> decltype(auto) get(_triqs_reversed_tuple<TU> const &t) {
78 return std::get<std::tuple_size_v<std::decay_t<TU>> - 1 - pos>(t._x);
79 }
80
81 template <int pos, typename TU> decltype(auto) get(_triqs_reversed_tuple<TU> &t) {
82 return std::get<std::tuple_size_v<std::decay_t<TU>> - 1 - pos>(t._x);
83 }
84
85 template <int pos, typename TU> decltype(auto) get(_triqs_reversed_tuple<TU> &&t) { // NOLINT
86 return std::get<std::tuple_size_v<std::decay_t<TU>> - 1 - pos>(std::move(t._x));
87 }
88
89 // Specialization of std::tuple_size for reversed tuples.
90 template <typename TU> class tuple_size<_triqs_reversed_tuple<TU>> : public tuple_size<std::decay_t<TU>> {};
91
92} // namespace std
93
94namespace triqs::tuple {
95
100
101 // Return the index sequence 0, 1, ..., tuple_size_v<T> - 1 corresponding to a given tuple type.
102 template <typename T> std::make_index_sequence<std::tuple_size_v<std::decay_t<T>>> _get_seq() { return {}; }
103
104 // Get the number of elements in a tuple-like type.
105 template <typename Tu> constexpr int _get_seq_len() { return std::tuple_size_v<std::decay_t<Tu>>; }
106
107 // Empty tag type carrying a compile-time integer N.
108 template <int N> struct _int {};
109
110 // Tag type inheriting from _int<Is> for a pack of indices Is.
111 template <int... Is> struct all_indices : _int<Is>... {};
112
113 // Recursively builds the complement of a set of indices, keeping those not in hole_seq and exposing them as ::type.
114 template <int N, typename hole_seq, int... Is> struct complement_sequence_impl {
115 static complement_sequence_impl<N - 1, hole_seq, Is...> get(_int<N>) {}
116 static complement_sequence_impl<N - 1, hole_seq, N, Is...> get(...) {}
117 using type = typename decltype(get(hole_seq()))::type;
118 };
119
120 // Base case of complement_sequence_impl: emit the accumulated indices.
121 template <typename hole_seq, int... Is> struct complement_sequence_impl<-1, hole_seq, Is...> {
122 using type = std::index_sequence<Is...>;
123 };
124
126 template <int N, int... Is> using complement_sequence = typename complement_sequence_impl<N, all_indices<Is...>>::type;
127
128 // Implementation of make_tuple_repeat().
129 template <typename T, std::size_t... Is> auto make_tuple_repeat_impl(T const &x, std::index_sequence<Is...>) {
130 return std::make_tuple(((void)Is, x)...);
131 }
132
141 template <int N, typename X> auto make_tuple_repeat(X const &x) { return make_tuple_repeat_impl(x, std::make_index_sequence<N>()); }
142
143 // Implementation of apply().
144 template <typename F, typename T, size_t... Is> decltype(auto) apply_impl(F &&f, T &&t, std::index_sequence<Is...>) {
145 return std::forward<F>(f)(std::get<Is>(std::forward<T>(t))...);
146 }
147
159 template <typename F, typename T> decltype(auto) apply(F &&f, T &&t) { return apply_impl(std::forward<F>(f), std::forward<T>(t), _get_seq<T>()); }
160
161 // Implementation of apply_construct().
162 template <typename C, typename T, size_t... Is> decltype(auto) apply_construct_impl(T &&t, std::index_sequence<Is...>) {
163 return C{std::get<Is>(std::forward<T>(t))...};
164 }
165
174 template <typename C, typename T> decltype(auto) apply_construct(T &&t) { return apply_construct_impl<C>(std::forward<T>(t), _get_seq<T>()); }
175
176 // Implementation of apply_construct_parenthesis().
177 template <typename C, typename T, size_t... Is> decltype(auto) apply_construct_parenthesis_impl(T &&t, std::index_sequence<Is...>) {
178 return C(std::get<Is>(std::forward<T>(t))...);
179 }
180
189 template <typename C, typename T> decltype(auto) apply_construct_parenthesis(T &&t) {
190 return apply_construct_parenthesis_impl<C>(std::forward<T>(t), _get_seq<T>());
191 }
192
193 // Wrapper turning a callable into one that accepts a single tuple and unpacks it via apply().
194 template <typename F> struct _called_on_tuple {
195 F _f;
196 template <typename Tu> decltype(auto) operator()(Tu &&tu) { return triqs::tuple::apply(_f, std::forward<Tu>(tu)); }
197 };
198
206 template <typename F> auto called_on_tuple(F &&f) { return _called_on_tuple<F>{std::forward<F>(f)}; }
207
208 // Base case for _for_each_impl().
209 template <typename F> void _for_each_impl(F &&) {}
210
211 // Call f(x0); f(x1); ...; f(xn); in this order.
212 template <typename F, typename T0, typename... T> void _for_each_impl(F &&f, T0 &&x0, T &&...x) { // NOLINT
213 f(std::forward<T0>(x0));
214 _for_each_impl(f, std::forward<T>(x)...);
215 }
216
217 // Base case for _for_each_apply_impl().
218 template <typename F> void _for_each_apply_impl(F &&) {}
219
220 // Call apply(f, t0); apply(f, t1); ...; apply(f, tn); in this order.
221 template <typename F, typename T0, typename... T> void _for_each_apply_impl(F &&f, T0 &&t0, T &&...t) { // NOLINT
222 triqs::tuple::apply(f, std::forward<T0>(t0));
223 _for_each_apply_impl(f, std::forward<T>(t)...);
224 }
225
226 // Implementation of for_each().
227 template <typename F, typename T, size_t... Is> void for_each_impl(F &&f, T &&t, std::index_sequence<Is...>) { // NOLINT
228 _for_each_impl(f, std::get<Is>(t)...);
229 }
230
239 template <typename T, typename F> void for_each(T &&t, F &&f) { for_each_impl(std::forward<F>(f), std::forward<T>(t), _get_seq<T>()); }
240
241 // Call f(n, x) for each element x at position n (Python's enumerate).
242 template <typename F, typename T, size_t... Is> void _for_each_enum_impl(F &&f, T &&t, std::index_sequence<Is...>) { // NOLINT
243 _for_each_apply_impl(f, std::tuple<int, decltype(std::get<Is>(t))>(Is, std::get<Is>(t))...);
244 }
245
256 template <typename T, typename F> void for_each_enumerate(T &&t, F &&f) {
257 _for_each_enum_impl(std::forward<F>(f), std::forward<T>(t), _get_seq<T>());
258 }
259
270 template <typename F, typename... Ts> void for_each_zip(F &&f, Ts &&...ts) {
271 for_each(zip_tuples(std::forward<Ts>(ts)...), called_on_tuple(std::forward<F>(f)));
272 }
273
274 // Build std::make_tuple(f(get<Is>(t))...) for map().
275 template <typename F, typename T, size_t... Is> decltype(auto) _map_impl(F &&f, T &&t, std::index_sequence<Is...>) {
276 return std::make_tuple(std::forward<F>(f)(std::get<Is>(std::forward<T>(t)))...);
277 }
278
288 template <typename F, typename T> decltype(auto) map(F &&f, T &&t) { return _map_impl(std::forward<F>(f), std::forward<T>(t), _get_seq<T>()); }
289
299 template <typename... Ts, typename F> auto map_on_zip(F &&f, Ts &&...ts) {
300 return map(called_on_tuple(std::forward<F>(f)), zip_tuples(std::forward<Ts>(ts)...));
301 }
302
303 // Recursive step of the single-tuple fold().
304 template <int pos, typename F, typename T, typename R> decltype(auto) fold_impl(_int<pos>, F &&f, T &&t, R &&r) {
305 return fold_impl(_int<pos - 1>(), std::forward<F>(f), std::forward<T>(t), f(std::get<_get_seq_len<T>() - 1 - pos>(t), std::forward<R>(r)));
306 }
307
308 // Base case of the single-tuple fold(): return the accumulator.
309 template <typename F, typename T, typename R> R fold_impl(_int<-1>, F &&, T &&, R &&r) { return std::forward<R>(r); }
310
324 template <typename F, typename T, typename R> decltype(auto) fold(F &&f, T &&t, R &&r) {
325 return fold_impl(_int<_get_seq_len<T>() - 1>(), std::forward<F>(f), std::forward<T>(t), std::forward<R>(r));
326 }
327
328 // Recursive step of the two-tuple fold().
329 template <int pos, typename F, typename T0, typename T1, typename R> decltype(auto) fold_impl(_int<pos>, F &&f, T0 &&t0, T1 &&t1, R &&r) {
330 constexpr int n = _get_seq_len<T0>() - 1 - pos;
331 return fold_impl(_int<pos - 1>(), std::forward<F>(f), std::forward<T0>(t0), std::forward<T1>(t1),
332 f(std::get<n>(t0), std::get<n>(t1), std::forward<R>(r)));
333 }
334
335 // Base case of the two-tuple fold(): return the accumulator.
336 template <typename F, typename T0, typename T1, typename R> R fold_impl(_int<-1>, F &&, T0 &&, T1 &&, R &&r) { return std::forward<R>(r); }
337
354 template <typename F, typename T0, typename T1, typename R> decltype(auto) fold(F &&f, T0 &&t1, T1 &&t2, R &&r) {
355 return fold_impl(_int<_get_seq_len<T0>() - 1>(), std::forward<F>(f), std::forward<T0>(t1), std::forward<T1>(t2), std::forward<R>(r));
356 }
357
358 // Return r (selected when index I is one of the replaced positions).
359 template <int I, typename T, typename R> R _get_rpl(T &&, R &&r, _int<I>) { return std::forward<R>(r); }
360
361 // Return the original element x (fallback for non-replaced positions).
362 // NOLINTNEXTLINE(modernize-avoid-variadic-functions) -- ellipsis is used as the lowest-priority overload.
363 template <int I, typename T, typename R> T _get_rpl(T &&x, R &&, ...) { return std::forward<T>(x); }
364
365 // Build the replaced tuple for replace(), choosing r at the positions listed in AllIndices.
366 template <size_t... Is, typename Tu, typename R, typename AllIndices>
367 auto _replace_impl(Tu &&tu, R &&r, AllIndices _, // NOLINT(cppcoreguidelines-missing-std-forward): r is reused per position, tu read element-wise
368 std::index_sequence<Is...>) {
369 return std::make_tuple(_get_rpl<Is>(std::get<Is>(tu), r, _)...);
370 }
371
382 template <int... Is, typename T, typename R> auto replace(T &&t, R &&r) {
383 return _replace_impl(std::forward<T>(t), std::forward<R>(r), all_indices<Is...>(), _get_seq<T>());
384 }
385
392 template <typename T, size_t... Is> using filter_t = std::tuple<std::tuple_element_t<Is, std::decay_t<T>>...>;
393
402 template <size_t... Is, typename T> filter_t<T, Is...> filter(T &&t) { return filter_t<T, Is...>(std::get<Is>(std::forward<T>(t))...); }
403
412 template <size_t... Is, typename T> filter_t<T, Is...> filter(T &&t, std::index_sequence<Is...>) {
413 return filter_t<T, Is...>(std::get<Is>(std::forward<T>(t))...);
414 }
415
424 template <int... Is, typename T> decltype(auto) filter_out(T &&t) {
425 return filter(std::forward<T>(t), complement_sequence<std::tuple_size_v<std::decay_t<T>> - 1, Is...>());
426 }
427
434 template <typename T, int... Is> using filter_out_t = std::decay_t<decltype(filter_out<Is...>(std::declval<T>()))>;
435
445 template <typename T, typename X> auto push_back(T &&t, X &&x) { return std::tuple_cat(std::forward<T>(t), std::make_tuple(std::forward<X>(x))); }
446
456 template <typename T, typename X> auto push_front(T &&t, X &&x) { return std::tuple_cat(std::make_tuple(std::forward<X>(x)), std::forward<T>(t)); }
457
465 template <typename T> auto pop_front(T &&t) { return filter_out<0>(std::forward<T>(t)); }
466
468
469} // namespace triqs::tuple
470
471namespace std {
472
477
486 template <typename... T> std::ostream &operator<<(std::ostream &os, std::tuple<T...> const &t) {
487 os << "(";
488 triqs::tuple::for_each(t, [&os, c = 0](auto &x) mutable {
489 if (c++) os << ',';
490 os << x;
491 });
492 return os << ")";
493 }
494
504 template <typename T1, typename T2> std::ostream &operator<<(std::ostream &os, std::pair<T1, T2> const &x) {
505 return os << '(' << x.first << ", " << x.second << ')';
506 }
507
509
510} // namespace std
std::ostream & operator<<(std::ostream &os, std::tuple< T... > const &t)
Write a std::tuple to an output stream.
void for_each(T &&t, F &&f)
Apply a callable to every element of a tuple, in order.
void for_each_zip(F &&f, Ts &&...ts)
Apply an N-ary callable across tuples zipped together.
typename complement_sequence_impl< N, all_indices< Is... > >::type complement_sequence
Index sequence containing all integers of that are not in Is....
auto make_tuple_repeat(X const &x)
Build a tuple with copies of a value.
std::decay_t< decltype(filter_out< Is... >(std::declval< T >()))> filter_out_t
Result type of filter_out() for tuple type T dropping positions Is....
decltype(auto) apply_construct_parenthesis(T &&t)
Parenthesis-construct an object from the elements of a tuple.
filter_t< T, Is... > filter(T &&t)
Keep only the elements of a tuple at the given positions.
std::tuple< std::tuple_element_t< Is, std::decay_t< T > >... > filter_t
Tuple type made of the elements at the given positions of given tuple type T.
decltype(auto) apply_construct(T &&t)
Brace-construct an object from the elements of a tuple.
auto called_on_tuple(F &&f)
Wrap a callable so that it can be invoked with a single tuple argument.
decltype(auto) filter_out(T &&t)
Drop the elements of a tuple at the given positions.
auto push_front(T &&t, X &&x)
Prepend an element to the front of a tuple.
decltype(auto) fold(F &&f, T &&t, R &&r)
Left-fold a callable over the elements of a tuple.
decltype(auto) map(F &&f, T &&t)
Map a callable over the elements of a tuple.
decltype(auto) apply(F &&f, T &&t)
Call a function with the elements of a tuple as its arguments.
auto replace(T &&t, R &&r)
Return a copy of a tuple with the elements at the given positions replaced by a given value.
auto map_on_zip(F &&f, Ts &&...ts)
Map an N-ary callable across N tuples zipped together.
auto push_back(T &&t, X &&x)
Append an element to the end of a tuple.
auto pop_front(T &&t)
Remove the first element of a tuple.
void for_each_enumerate(T &&t, F &&f)
Apply a callable to every (index, element) pair of a tuple.
Common macros used in TRIQS.