• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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