v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
template-utils.h
Go to the documentation of this file.
1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_BASE_TEMPLATE_UTILS_H_
6#define V8_BASE_TEMPLATE_UTILS_H_
7
8#include <stddef.h>
9
10#include <array>
11#include <functional>
12#include <iosfwd>
13#include <tuple>
14#include <type_traits>
15#include <utility>
16
17namespace v8 {
18namespace base {
19
20namespace detail {
21
22template <typename Function, std::size_t... Indexes>
23constexpr inline auto make_array_helper(Function f,
24 std::index_sequence<Indexes...>)
25 -> std::array<decltype(f(0)), sizeof...(Indexes)> {
26 return {{f(Indexes)...}};
27}
28
29} // namespace detail
30
31// base::make_array: Create an array of fixed length, initialized by a function.
32// The content of the array is created by calling the function with 0 .. Size-1.
33// Example usage to create the array {0, 2, 4}:
34// std::array<int, 3> arr = base::make_array<3>(
35// [](std::size_t i) { return static_cast<int>(2 * i); });
36// The resulting array will be constexpr if the passed function is constexpr.
37template <std::size_t Size, class Function>
38constexpr auto make_array(Function f) {
39 return detail::make_array_helper(f, std::make_index_sequence<Size>{});
40}
41
42// base::overloaded: Create a callable which wraps a collection of other
43// callables, and treats them as an overload set. A typical use case would
44// be passing a collection of lambda functions to templated code which could
45// call them with different argument types, e.g.
46//
47// CallWithIntOrDouble(base::overloaded{
48// [&] (int val) { process_int(val); }
49// [&] (double val) { process_double(val); }
50// });
51template <class... Ts>
52struct overloaded : Ts... {
53 using Ts::operator()...;
54};
55template <class... Ts>
56overloaded(Ts...) -> overloaded<Ts...>;
57
58// Helper to determine how to pass values: Pass scalars and arrays by value,
59// others by const reference (even if it was a non-const ref before; this is
60// disallowed by the style guide anyway).
61// The default is to also remove array extends (int[5] -> int*), but this can be
62// disabled by setting {remove_array_extend} to false.
63template <typename T, bool remove_array_extend = true>
65 using noref_t = typename std::remove_reference<T>::type;
66 using decay_t = typename std::conditional<
67 std::is_array<noref_t>::value && !remove_array_extend, noref_t,
68 typename std::decay<noref_t>::type>::type;
69 using type = typename std::conditional<std::is_scalar<decay_t>::value ||
70 std::is_array<decay_t>::value,
71 decay_t, const decay_t&>::type;
72};
73
74template <typename T, typename TStream = std::ostream>
75concept has_output_operator = requires(T t, TStream stream) { stream << t; };
76
77// Turn std::tuple<A...> into std::tuple<A..., T>.
78template <class Tuple, class T>
79using append_tuple_type = decltype(std::tuple_cat(
80 std::declval<Tuple>(), std::declval<std::tuple<T>>()));
81
82// Turn std::tuple<A...> into std::tuple<T, A...>.
83template <class T, class Tuple>
84using prepend_tuple_type = decltype(std::tuple_cat(
85 std::declval<std::tuple<T>>(), std::declval<Tuple>()));
86
87namespace detail {
88
89template <size_t N, typename Tuple>
91 N <= std::tuple_size_v<std::decay_t<Tuple>>;
92
93template <size_t N, typename T, size_t... Ints>
94constexpr auto tuple_slice_impl(const T& tpl, std::index_sequence<Ints...>) {
95 return std::tuple{std::get<N + Ints>(tpl)...};
96}
97
98template <typename Tuple, typename Function, size_t... Index>
99constexpr auto tuple_for_each_impl(const Tuple& tpl, Function&& function,
100 std::index_sequence<Index...>) {
101 (function(std::get<Index>(tpl)), ...);
102}
103
104template <typename Tuple, typename Function, size_t... Index>
105constexpr auto tuple_for_each_with_index_impl(const Tuple& tpl,
106 Function&& function,
107 std::index_sequence<Index...>) {
108 (function(std::get<Index>(tpl), std::integral_constant<size_t, Index>()),
109 ...);
110}
111
112template <typename Tuple, typename Function, size_t... Index>
113constexpr auto tuple_map_impl(Tuple&& tpl, const Function& function,
114 std::index_sequence<Index...>) {
115 return std::make_tuple(
116 function(std::get<Index>(std::forward<Tuple>(tpl)))...);
117}
118
119template <typename TupleV, typename TupleU, typename Function, size_t... Index>
120constexpr auto tuple_map2_impl(TupleV&& tplv, TupleU&& tplu,
121 const Function& function,
122 std::index_sequence<Index...>) {
123 return std::make_tuple(
124 function(std::get<Index>(tplv), std::get<Index>(tplu))...);
125}
126
127template <size_t I, typename T, typename Tuple, typename Function>
128constexpr auto tuple_fold_impl(T&& initial, Tuple&& tpl, Function&& function) {
129 if constexpr (I == 0) {
130 return function(std::forward<T>(initial), std::get<0>(tpl));
131 } else {
132 return function(tuple_fold_impl<I - 1>(std::forward<T>(initial),
133 std::forward<Tuple>(tpl), function),
134 std::get<I>(tpl));
135 }
136}
137
138} // namespace detail
139
140// Get the first N elements from a tuple.
141template <size_t N, typename Tuple>
142constexpr auto tuple_head(Tuple&& tpl) {
143 constexpr size_t total_size = std::tuple_size_v<std::decay_t<Tuple>>;
144 static_assert(N <= total_size);
145 return detail::tuple_slice_impl<0>(std::forward<Tuple>(tpl),
146 std::make_index_sequence<N>());
147}
148
149// Drop the first N elements from a tuple.
150template <size_t N, typename Tuple>
151// If the user accidentally passes in an N that is larger than the tuple
152// size, the unsigned subtraction will create a giant index sequence and
153// crash the compiler. To avoid this and fail early, disable this function
154// for invalid N.
156constexpr auto tuple_drop(Tuple&& tpl) {
157 constexpr size_t total_size = std::tuple_size_v<std::decay_t<Tuple>>;
158 static_assert(N <= total_size);
160 std::forward<Tuple>(tpl), std::make_index_sequence<total_size - N>());
161}
162
163// Calls `function(v)` for each `v` in the tuple.
164template <typename Tuple, typename Function>
165constexpr void tuple_for_each(Tuple&& tpl, Function&& function) {
167 std::forward<Tuple>(tpl), function,
168 std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
169}
170
171// Calls `function(v, i)` for each `v` in the tuple, with index `i`. The index
172// `i` is passed as an std::integral_constant<size_t>, rather than a raw size_t,
173// to allow it to be used
174template <typename Tuple, typename Function>
175constexpr void tuple_for_each_with_index(Tuple&& tpl, Function&& function) {
177 std::forward<Tuple>(tpl), function,
178 std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
179}
180
181// Calls `function(v)` for each `v` in the tuple and returns a new tuple with
182// all the results.
183template <typename Tuple, typename Function>
184constexpr auto tuple_map(Tuple&& tpl, Function&& function) {
186 std::forward<Tuple>(tpl), function,
187 std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
188}
189
190// Calls `function(v, u)` for pairs `v<I>, u<I>` in the
191// tuples and returns a new tuple with all the results.
192template <typename TupleV, typename TupleU, typename Function>
193constexpr auto tuple_map2(TupleV&& tplv, TupleU&& tplu, Function&& function) {
194 constexpr size_t S = std::tuple_size_v<std::decay_t<TupleV>>;
195 static_assert(S == std::tuple_size_v<std::decay_t<TupleU>>);
196 return detail::tuple_map2_impl(std::forward<TupleV>(tplv),
197 std::forward<TupleU>(tplu), function,
198 std::make_index_sequence<S>());
199}
200
201// Left fold (reduce) the tuple starting with an initial value by applying
202// function(...function(initial, tpl<0>)..., tpl<size-1>)
203template <typename T, typename Tuple, typename Function>
204constexpr auto tuple_fold(T&& initial, Tuple&& tpl, Function&& function) {
206 std::forward<T>(initial), std::forward<Tuple>(tpl), function);
207}
208
209#ifdef __clang__
210
211template <size_t N, typename... Ts>
212using nth_type_t = __type_pack_element<N, Ts...>;
213
214#else
215
216namespace detail {
217template <size_t N, typename... Ts>
218struct nth_type;
219
220template <typename T, typename... Ts>
221struct nth_type<0, T, Ts...> {
222 using type = T;
223};
224
225template <size_t N, typename T, typename... Ts>
226struct nth_type<N, T, Ts...> : public nth_type<N - 1, Ts...> {};
227} // namespace detail
228
229template <size_t N, typename... T>
230using nth_type_t = typename detail::nth_type<N, T...>::type;
231
232#endif
233
234// Find SearchT in Ts. SearchT must be present at most once in Ts, and returns
235// sizeof...(Ts) if not found.
236template <typename SearchT, typename... Ts>
238
239template <typename SearchT, typename... Ts>
240constexpr size_t index_of_type_v = index_of_type<SearchT, Ts...>::value;
241template <typename SearchT, typename... Ts>
242constexpr bool has_type_v =
243 index_of_type<SearchT, Ts...>::value < sizeof...(Ts);
244
245// Not found / empty list.
246template <typename SearchT>
247struct index_of_type<SearchT> : public std::integral_constant<size_t, 0> {};
248
249// SearchT found at head of list.
250template <typename SearchT, typename... Ts>
251struct index_of_type<SearchT, SearchT, Ts...>
252 : public std::integral_constant<size_t, 0> {
253 // SearchT is not allowed to be anywhere else in the list.
254 static_assert(!has_type_v<SearchT, Ts...>);
255};
256
257// Recursion, SearchT not found at head of list.
258template <typename SearchT, typename T, typename... Ts>
259struct index_of_type<SearchT, T, Ts...>
260 : public std::integral_constant<size_t,
261 1 + index_of_type<SearchT, Ts...>::value> {
262};
263
264} // namespace base
265} // namespace v8
266
267#endif // V8_BASE_TEMPLATE_UTILS_H_
#define T
constexpr auto tuple_fold_impl(T &&initial, Tuple &&tpl, Function &&function)
constexpr auto tuple_map2_impl(TupleV &&tplv, TupleU &&tplu, const Function &function, std::index_sequence< Index... >)
constexpr auto tuple_map_impl(Tuple &&tpl, const Function &function, std::index_sequence< Index... >)
constexpr auto make_array_helper(Function f, std::index_sequence< Indexes... >) -> std::array< decltype(f(0)), sizeof...(Indexes)>
constexpr auto tuple_for_each_impl(const Tuple &tpl, Function &&function, std::index_sequence< Index... >)
constexpr auto tuple_for_each_with_index_impl(const Tuple &tpl, Function &&function, std::index_sequence< Index... >)
constexpr bool NIsNotGreaterThanTupleSize
constexpr auto tuple_slice_impl(const T &tpl, std::index_sequence< Ints... >)
constexpr size_t index_of_type_v
constexpr auto make_array(Function f)
constexpr auto tuple_head(Tuple &&tpl)
constexpr auto tuple_fold(T &&initial, Tuple &&tpl, Function &&function)
decltype(std::tuple_cat( std::declval< Tuple >(), std::declval< std::tuple< T > >())) append_tuple_type
decltype(std::tuple_cat( std::declval< std::tuple< T > >(), std::declval< Tuple >())) prepend_tuple_type
constexpr auto tuple_map(Tuple &&tpl, Function &&function)
constexpr auto tuple_map2(TupleV &&tplv, TupleU &&tplu, Function &&function)
constexpr void tuple_for_each_with_index(Tuple &&tpl, Function &&function)
constexpr bool has_type_v
typename detail::nth_type< N, T... >::type nth_type_t
constexpr auto tuple_drop(Tuple &&tpl)
overloaded(Ts...) -> overloaded< Ts... >
constexpr void tuple_for_each(Tuple &&tpl, Function &&function)
#define I(name, number_of_args, result_size)
Definition runtime.cc:36
typename std::remove_reference< T >::type noref_t
typename std::conditional< std::is_scalar< decay_t >::value|| std::is_array< decay_t >::value, decay_t, const decay_t & >::type type
typename std::conditional< std::is_array< noref_t >::value &&!remove_array_extend, noref_t, typename std::decay< noref_t >::type >::type decay_t
std::unique_ptr< ValueMirror > value
wasm::ValueType type