Dominik Laskowski | ccd50a4 | 2020-10-30 19:56:38 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 The Android Open Source Project |
| 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 <tuple> |
| 20 | #include <utility> |
| 21 | |
| 22 | namespace android::ftl { |
| 23 | |
| 24 | // Compile-time counterpart of std::initializer_list<T> that stores per-element constructor |
| 25 | // arguments with heterogeneous types. For a container with elements of type T, given Sizes |
| 26 | // (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the |
| 27 | // first S0 arguments, the second element is initialized with the next S1 arguments, and so |
| 28 | // on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes. |
| 29 | // |
| 30 | // The InitializerList is created using ftl::init::list, and is consumed by constructors of |
| 31 | // containers. The function call operator is overloaded such that arguments are accumulated |
| 32 | // in a tuple with each successive call. For instance, the following calls initialize three |
| 33 | // strings using different constructors, i.e. string literal, default, and count/character: |
| 34 | // |
| 35 | // ... = ftl::init::list<std::string>("abc")()(3u, '?'); |
| 36 | // |
Dominik Laskowski | c4b9146 | 2020-11-02 13:37:25 -0800 | [diff] [blame] | 37 | // The following syntax is a shorthand for key-value pairs, where the first argument is the |
| 38 | // key, and the rest construct the value. The types of the key and value are deduced if the |
| 39 | // first pair contains exactly two arguments: |
| 40 | // |
| 41 | // ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?'); |
| 42 | // |
| 43 | // ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); |
| 44 | // |
Dominik Laskowski | ccd50a4 | 2020-10-30 19:56:38 -0700 | [diff] [blame] | 45 | // WARNING: The InitializerList returned by an ftl::init::list expression must be consumed |
| 46 | // immediately, since temporary arguments are destroyed after the full expression. Storing |
| 47 | // an InitializerList results in dangling references. |
| 48 | // |
| 49 | template <typename T, typename Sizes = std::index_sequence<>, typename... Types> |
| 50 | struct InitializerList; |
| 51 | |
| 52 | template <typename T, size_t... Sizes, typename... Types> |
| 53 | struct InitializerList<T, std::index_sequence<Sizes...>, Types...> { |
| 54 | // Creates a superset InitializerList by appending the number of arguments to Sizes, and |
| 55 | // expanding Types with forwarding references for each argument. |
| 56 | template <typename... Args> |
| 57 | [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList< |
| 58 | T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> { |
| 59 | return {std::tuple_cat(std::move(tuple), |
| 60 | std::forward_as_tuple(std::forward<Args>(args)...))}; |
| 61 | } |
| 62 | |
| 63 | // The temporary InitializerList returned by operator() is bound to an rvalue reference in |
| 64 | // container constructors, which extends the lifetime of any temporary arguments that this |
| 65 | // tuple refers to until the completion of the full expression containing the construction. |
| 66 | std::tuple<Types...> tuple; |
| 67 | }; |
| 68 | |
Dominik Laskowski | c4b9146 | 2020-11-02 13:37:25 -0800 | [diff] [blame] | 69 | template <typename K, typename V> |
| 70 | struct KeyValue {}; |
| 71 | |
| 72 | // Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the |
| 73 | // value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works |
| 74 | // with the latter. |
| 75 | template <typename K, typename V, size_t... Sizes, typename... Types> |
| 76 | struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> { |
| 77 | // Accumulate the three arguments to std::pair's piecewise constructor. |
| 78 | template <typename... Args> |
| 79 | [[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList< |
| 80 | KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, |
| 81 | std::tuple<K&&>, std::tuple<Args&&...>> { |
| 82 | return {std::tuple_cat(std::move(tuple), |
| 83 | std::forward_as_tuple(std::piecewise_construct, |
| 84 | std::forward_as_tuple(std::forward<K>(k)), |
| 85 | std::forward_as_tuple( |
| 86 | std::forward<Args>(args)...)))}; |
| 87 | } |
| 88 | |
| 89 | std::tuple<Types...> tuple; |
| 90 | }; |
| 91 | |
Dominik Laskowski | ccd50a4 | 2020-10-30 19:56:38 -0700 | [diff] [blame] | 92 | namespace init { |
| 93 | |
| 94 | template <typename T, typename... Args> |
| 95 | [[nodiscard]] constexpr auto list(Args&&... args) { |
| 96 | return InitializerList<T>{}(std::forward<Args>(args)...); |
| 97 | } |
| 98 | |
Dominik Laskowski | c4b9146 | 2020-11-02 13:37:25 -0800 | [diff] [blame] | 99 | template <typename K, typename V, typename... Args> |
| 100 | [[nodiscard]] constexpr auto map(Args&&... args) { |
| 101 | return list<KeyValue<K, V>>(std::forward<Args>(args)...); |
| 102 | } |
| 103 | |
| 104 | template <typename K, typename V> |
| 105 | [[nodiscard]] constexpr auto map(K&& k, V&& v) { |
| 106 | return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v)); |
| 107 | } |
| 108 | |
Dominik Laskowski | ccd50a4 | 2020-10-30 19:56:38 -0700 | [diff] [blame] | 109 | } // namespace init |
| 110 | } // namespace android::ftl |