blob: 5d1acb14d00b8650159c55c6b448872ae5066f2e [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_BASE_TEMPLATE_UTILS_H_
#define V8_BASE_TEMPLATE_UTILS_H_
#include <stddef.h>
#include <array>
#include <functional>
#include <iosfwd>
#include <tuple>
#include <type_traits>
#include <utility>
namespace v8 {
namespace base {
namespace detail {
template <typename Function, std::size_t... Indexes>
constexpr inline auto make_array_helper(Function f,
std::index_sequence<Indexes...>)
-> std::array<decltype(f(0)), sizeof...(Indexes)> {
return {{f(Indexes)...}};
}
} // namespace detail
// base::make_array: Create an array of fixed length, initialized by a function.
// The content of the array is created by calling the function with 0 .. Size-1.
// Example usage to create the array {0, 2, 4}:
// std::array<int, 3> arr = base::make_array<3>(
// [](std::size_t i) { return static_cast<int>(2 * i); });
// The resulting array will be constexpr if the passed function is constexpr.
template <std::size_t Size, class Function>
constexpr auto make_array(Function f) {
return detail::make_array_helper(f, std::make_index_sequence<Size>{});
}
// base::overloaded: Create a callable which wraps a collection of other
// callables, and treats them as an overload set. A typical use case would
// be passing a collection of lambda functions to templated code which could
// call them with different argument types, e.g.
//
// CallWithIntOrDouble(base::overloaded{
// [&] (int val) { process_int(val); }
// [&] (double val) { process_double(val); }
// });
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
// Helper to determine how to pass values: Pass scalars and arrays by value,
// others by const reference (even if it was a non-const ref before; this is
// disallowed by the style guide anyway).
// The default is to also remove array extends (int[5] -> int*), but this can be
// disabled by setting {remove_array_extend} to false.
template <typename T, bool remove_array_extend = true>
struct pass_value_or_ref {
using noref_t = typename std::remove_reference<T>::type;
using decay_t = typename std::conditional<
std::is_array<noref_t>::value && !remove_array_extend, noref_t,
typename std::decay<noref_t>::type>::type;
using type = typename std::conditional<std::is_scalar<decay_t>::value ||
std::is_array<decay_t>::value,
decay_t, const decay_t&>::type;
};
template <typename T, typename TStream = std::ostream>
concept has_output_operator = requires(T t, TStream stream) { stream << t; };
// Turn std::tuple<A...> into std::tuple<A..., T>.
template <class Tuple, class T>
using append_tuple_type = decltype(std::tuple_cat(
std::declval<Tuple>(), std::declval<std::tuple<T>>()));
// Turn std::tuple<A...> into std::tuple<T, A...>.
template <class T, class Tuple>
using prepend_tuple_type = decltype(std::tuple_cat(
std::declval<std::tuple<T>>(), std::declval<Tuple>()));
namespace detail {
template <size_t N, typename Tuple>
constexpr bool NIsNotGreaterThanTupleSize =
N <= std::tuple_size_v<std::decay_t<Tuple>>;
template <size_t N, typename T, size_t... Ints>
constexpr auto tuple_slice_impl(const T& tpl, std::index_sequence<Ints...>) {
return std::tuple{std::get<N + Ints>(tpl)...};
}
template <typename Tuple, typename Function, size_t... Index>
constexpr auto tuple_for_each_impl(const Tuple& tpl, Function&& function,
std::index_sequence<Index...>) {
(function(std::get<Index>(tpl)), ...);
}
template <typename Tuple, typename Function, size_t... Index>
constexpr auto tuple_for_each_with_index_impl(const Tuple& tpl,
Function&& function,
std::index_sequence<Index...>) {
(function(std::get<Index>(tpl), std::integral_constant<size_t, Index>()),
...);
}
template <typename Tuple, typename Function, size_t... Index>
constexpr auto tuple_map_impl(Tuple&& tpl, const Function& function,
std::index_sequence<Index...>) {
return std::make_tuple(
function(std::get<Index>(std::forward<Tuple>(tpl)))...);
}
template <typename TupleV, typename TupleU, typename Function, size_t... Index>
constexpr auto tuple_map2_impl(TupleV&& tplv, TupleU&& tplu,
const Function& function,
std::index_sequence<Index...>) {
return std::make_tuple(
function(std::get<Index>(tplv), std::get<Index>(tplu))...);
}
template <size_t I, typename T, typename Tuple, typename Function>
constexpr auto tuple_fold_impl(T&& initial, Tuple&& tpl, Function&& function) {
if constexpr (I == 0) {
return function(std::forward<T>(initial), std::get<0>(tpl));
} else {
return function(tuple_fold_impl<I - 1>(std::forward<T>(initial),
std::forward<Tuple>(tpl), function),
std::get<I>(tpl));
}
}
} // namespace detail
// Get the first N elements from a tuple.
template <size_t N, typename Tuple>
constexpr auto tuple_head(Tuple&& tpl) {
constexpr size_t total_size = std::tuple_size_v<std::decay_t<Tuple>>;
static_assert(N <= total_size);
return detail::tuple_slice_impl<0>(std::forward<Tuple>(tpl),
std::make_index_sequence<N>());
}
// Drop the first N elements from a tuple.
template <size_t N, typename Tuple>
// If the user accidentally passes in an N that is larger than the tuple
// size, the unsigned subtraction will create a giant index sequence and
// crash the compiler. To avoid this and fail early, disable this function
// for invalid N.
requires(detail::NIsNotGreaterThanTupleSize<N, Tuple>)
constexpr auto tuple_drop(Tuple&& tpl) {
constexpr size_t total_size = std::tuple_size_v<std::decay_t<Tuple>>;
static_assert(N <= total_size);
return detail::tuple_slice_impl<N>(
std::forward<Tuple>(tpl), std::make_index_sequence<total_size - N>());
}
// Calls `function(v)` for each `v` in the tuple.
template <typename Tuple, typename Function>
constexpr void tuple_for_each(Tuple&& tpl, Function&& function) {
detail::tuple_for_each_impl(
std::forward<Tuple>(tpl), function,
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}
// Calls `function(v, i)` for each `v` in the tuple, with index `i`. The index
// `i` is passed as an std::integral_constant<size_t>, rather than a raw size_t,
// to allow it to be used
template <typename Tuple, typename Function>
constexpr void tuple_for_each_with_index(Tuple&& tpl, Function&& function) {
detail::tuple_for_each_with_index_impl(
std::forward<Tuple>(tpl), function,
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}
// Calls `function(v)` for each `v` in the tuple and returns a new tuple with
// all the results.
template <typename Tuple, typename Function>
constexpr auto tuple_map(Tuple&& tpl, Function&& function) {
return detail::tuple_map_impl(
std::forward<Tuple>(tpl), function,
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
}
// Calls `function(v, u)` for pairs `v<I>, u<I>` in the
// tuples and returns a new tuple with all the results.
template <typename TupleV, typename TupleU, typename Function>
constexpr auto tuple_map2(TupleV&& tplv, TupleU&& tplu, Function&& function) {
constexpr size_t S = std::tuple_size_v<std::decay_t<TupleV>>;
static_assert(S == std::tuple_size_v<std::decay_t<TupleU>>);
return detail::tuple_map2_impl(std::forward<TupleV>(tplv),
std::forward<TupleU>(tplu), function,
std::make_index_sequence<S>());
}
// Left fold (reduce) the tuple starting with an initial value by applying
// function(...function(initial, tpl<0>)..., tpl<size-1>)
template <typename T, typename Tuple, typename Function>
constexpr auto tuple_fold(T&& initial, Tuple&& tpl, Function&& function) {
return detail::tuple_fold_impl<std::tuple_size_v<std::decay_t<Tuple>> - 1>(
std::forward<T>(initial), std::forward<Tuple>(tpl), function);
}
#ifdef __clang__
template <size_t N, typename... Ts>
using nth_type_t = __type_pack_element<N, Ts...>;
#else
namespace detail {
template <size_t N, typename... Ts>
struct nth_type;
template <typename T, typename... Ts>
struct nth_type<0, T, Ts...> {
using type = T;
};
template <size_t N, typename T, typename... Ts>
struct nth_type<N, T, Ts...> : public nth_type<N - 1, Ts...> {};
} // namespace detail
template <size_t N, typename... T>
using nth_type_t = typename detail::nth_type<N, T...>::type;
#endif
// Find SearchT in Ts. SearchT must be present at most once in Ts, and returns
// sizeof...(Ts) if not found.
template <typename SearchT, typename... Ts>
struct index_of_type;
template <typename SearchT, typename... Ts>
constexpr size_t index_of_type_v = index_of_type<SearchT, Ts...>::value;
template <typename SearchT, typename... Ts>
constexpr bool has_type_v =
index_of_type<SearchT, Ts...>::value < sizeof...(Ts);
// Not found / empty list.
template <typename SearchT>
struct index_of_type<SearchT> : public std::integral_constant<size_t, 0> {};
// SearchT found at head of list.
template <typename SearchT, typename... Ts>
struct index_of_type<SearchT, SearchT, Ts...>
: public std::integral_constant<size_t, 0> {
// SearchT is not allowed to be anywhere else in the list.
static_assert(!has_type_v<SearchT, Ts...>);
};
// Recursion, SearchT not found at head of list.
template <typename SearchT, typename T, typename... Ts>
struct index_of_type<SearchT, T, Ts...>
: public std::integral_constant<size_t,
1 + index_of_type<SearchT, Ts...>::value> {
};
} // namespace base
} // namespace v8
#endif // V8_BASE_TEMPLATE_UTILS_H_