| 1 | /* |
| 2 | * Copyright 2015-present Facebook, Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #pragma once |
| 18 | |
| 19 | #include <limits> |
| 20 | #include <tuple> |
| 21 | #include <type_traits> |
| 22 | |
| 23 | // tupleRange<start, n>(tuple): select n elements starting at index start |
| 24 | // in the given tuple |
| 25 | // tupleRange<start>(tuple): select all elements starting at index start |
| 26 | // until the end of the given tuple |
| 27 | // tuplePrepend(x, tuple): return a tuple obtained by prepending x to the |
| 28 | // given tuple. |
| 29 | // |
| 30 | // In Lisp lingo, std::get<0> is car, tupleRange<1> is cdr, and tuplePrepend |
| 31 | // is cons. |
| 32 | |
| 33 | namespace folly { |
| 34 | |
| 35 | // TemplateSeq<T, ...> is a type parametrized by sizeof...(Xs) values of type |
| 36 | // T. Used to destructure the values into a template parameter pack; |
| 37 | // see the example in TupleSelect, below. |
| 38 | template <class T, T... xs> |
| 39 | struct TemplateSeq { |
| 40 | template <T x> |
| 41 | using Prepend = TemplateSeq<T, x, xs...>; |
| 42 | }; |
| 43 | |
| 44 | // TemplateRange<T, start, n>::type is |
| 45 | // TemplateSeq<T, start+1, start+2, ..., start+n-1> |
| 46 | template <class T, T start, T n, class Enable = void> |
| 47 | struct TemplateRange; |
| 48 | |
| 49 | template <class T, T start, T n> |
| 50 | struct TemplateRange<T, start, n, typename std::enable_if<(n > 0)>::type> { |
| 51 | using type = |
| 52 | typename TemplateRange<T, start + 1, n - 1>::type::template Prepend< |
| 53 | start>; |
| 54 | }; |
| 55 | |
| 56 | template <class T, T start, T n> |
| 57 | struct TemplateRange<T, start, n, typename std::enable_if<(n <= 0)>::type> { |
| 58 | using type = TemplateSeq<T>; |
| 59 | }; |
| 60 | |
| 61 | // Similar to TemplateRange, given a tuple T, |
| 62 | // TemplateTupleRange<T, start, n>::type is |
| 63 | // TemplateSeq<size_t, start, start+1, ..., start+k-1> |
| 64 | // where k = min(tuple_size<T>::value - start, n) |
| 65 | // (that is, it's a TemplateSeq of at most n elements, but won't extend |
| 66 | // past the end of the given tuple) |
| 67 | template < |
| 68 | class T, |
| 69 | std::size_t start = 0, |
| 70 | std::size_t n = std::numeric_limits<std::size_t>::max(), |
| 71 | std::size_t size = |
| 72 | std::tuple_size<typename std::remove_reference<T>::type>::value, |
| 73 | class Enable = typename std::enable_if<(start <= size)>::type> |
| 74 | struct TemplateTupleRange { |
| 75 | using type = typename TemplateRange< |
| 76 | std::size_t, |
| 77 | start, |
| 78 | (n <= size - start ? n : size - start)>::type; |
| 79 | }; |
| 80 | |
| 81 | namespace detail { |
| 82 | |
| 83 | // Helper class to select a subset of a tuple |
| 84 | template <class S> |
| 85 | struct TupleSelect; |
| 86 | template <std::size_t... Ns> |
| 87 | struct TupleSelect<TemplateSeq<std::size_t, Ns...>> { |
| 88 | template <class T> |
| 89 | static auto select(T&& v) |
| 90 | -> decltype(std::make_tuple(std::get<Ns>(std::forward<T>(v))...)) { |
| 91 | return std::make_tuple(std::get<Ns>(std::forward<T>(v))...); |
| 92 | } |
| 93 | }; |
| 94 | |
| 95 | } // namespace detail |
| 96 | |
| 97 | // Return a tuple consisting of the elements at a range of indices. |
| 98 | // |
| 99 | // Use as tupleRange<start, n>(t) to return a tuple of (at most) n |
| 100 | // elements starting at index start in tuple t. |
| 101 | // If only start is specified (tupleRange<start>(t)), returns all elements |
| 102 | // starting at index start until the end of the tuple t. |
| 103 | // Won't compile if start > size of t. |
| 104 | // Will return fewer elements (size - start) if start + n > size of t. |
| 105 | template < |
| 106 | std::size_t start = 0, |
| 107 | std::size_t n = std::numeric_limits<std::size_t>::max(), |
| 108 | class T, |
| 109 | class Seq = typename TemplateTupleRange<T, start, n>::type> |
| 110 | auto tupleRange(T&& v) |
| 111 | -> decltype(detail::TupleSelect<Seq>::select(std::forward<T>(v))) { |
| 112 | return detail::TupleSelect<Seq>::select(std::forward<T>(v)); |
| 113 | } |
| 114 | |
| 115 | // Return a tuple obtained by prepending car to the tuple cdr. |
| 116 | template <class T, class U> |
| 117 | auto tuplePrepend(T&& car, U&& cdr) -> decltype(std::tuple_cat( |
| 118 | std::make_tuple(std::forward<T>(car)), |
| 119 | std::forward<U>(cdr))) { |
| 120 | return std::tuple_cat( |
| 121 | std::make_tuple(std::forward<T>(car)), std::forward<U>(cdr)); |
| 122 | } |
| 123 | |
| 124 | } // namespace folly |
| 125 | |