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 <functional> 20 #include <tuple> 21 #include <utility> 22 23 namespace android::ftl { 24 25 // Compile-time counterpart of std::initializer_list<T> that stores per-element constructor 26 // arguments with heterogeneous types. For a container with elements of type T, given Sizes 27 // (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the 28 // first S0 arguments, the second element is initialized with the next S1 arguments, and so 29 // on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes. 30 // 31 // An InitializerList is created using ftl::init::list, and is consumed by constructors of 32 // containers. The function call operator is overloaded such that arguments are accumulated 33 // in a tuple with each successive call. For instance, the following calls initialize three 34 // strings using different constructors, i.e. string literal, default, and count/character: 35 // 36 // ... = ftl::init::list<std::string>("abc")()(3u, '?'); 37 // 38 // The following syntax is a shorthand for key-value pairs, where the first argument is the 39 // key, and the rest construct the value. The types of the key and value are deduced if the 40 // first pair contains exactly two arguments: 41 // 42 // ... = ftl::init::map<int, std::string>(-1, "abc")(-2)(-3, 3u, '?'); 43 // 44 // ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); 45 // 46 // WARNING: The InitializerList returned by an ftl::init::list expression must be consumed 47 // immediately, since temporary arguments are destroyed after the full expression. Storing 48 // an InitializerList results in dangling references. 49 // 50 template <typename T, typename Sizes = std::index_sequence<>, typename... Types> 51 struct InitializerList; 52 53 template <typename T, std::size_t... Sizes, typename... Types> 54 struct InitializerList<T, std::index_sequence<Sizes...>, Types...> { 55 // Creates a superset InitializerList by appending the number of arguments to Sizes, and 56 // expanding Types with forwarding references for each argument. 57 template <typename... Args> 58 [[nodiscard]] constexpr auto operator()(Args&&... args) && -> InitializerList< 59 T, std::index_sequence<Sizes..., sizeof...(Args)>, Types..., Args&&...> { 60 return {std::tuple_cat(std::move(tuple), 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 69 template <typename K, typename V, typename KeyEqual = std::equal_to<K>> 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, typename E, std::size_t... Sizes, typename... Types> 76 struct InitializerList<KeyValue<K, V, E>, 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, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t, 81 std::tuple<K&&>, std::tuple<Args&&...>> { 82 return {std::tuple_cat( 83 std::move(tuple), 84 std::forward_as_tuple(std::piecewise_construct, std::forward_as_tuple(std::forward<K>(k)), 85 std::forward_as_tuple(std::forward<Args>(args)...)))}; 86 } 87 88 std::tuple<Types...> tuple; 89 }; 90 91 namespace init { 92 93 template <typename T, typename... Args> 94 [[nodiscard]] constexpr auto list(Args&&... args) { 95 return InitializerList<T>{}(std::forward<Args>(args)...); 96 } 97 98 template <typename K, typename V, typename E = std::equal_to<K>, typename... Args> 99 [[nodiscard]] constexpr auto map(Args&&... args) { 100 return list<KeyValue<K, V, E>>(std::forward<Args>(args)...); 101 } 102 103 template <typename K, typename V> 104 [[nodiscard]] constexpr auto map(K&& k, V&& v) { 105 return list<KeyValue<K, V>>(std::forward<K>(k), std::forward<V>(v)); 106 } 107 108 } // namespace init 109 } // namespace android::ftl 110