Spaces:
Running
Running
namespace c10::guts { | |
/** | |
* Access information about result type or arguments from a function type. | |
* Example: | |
* using A = function_traits<int (float, double)>::return_type // A == int | |
* using A = function_traits<int (float, double)>::parameter_types::tuple_type | |
* // A == tuple<float, double> | |
*/ | |
template <class Func> | |
struct function_traits { | |
static_assert( | |
!std::is_same_v<Func, Func>, | |
"In function_traits<Func>, Func must be a plain function type."); | |
}; | |
template <class Result, class... Args> | |
struct function_traits<Result(Args...)> { | |
using func_type = Result(Args...); | |
using return_type = Result; | |
using parameter_types = typelist::typelist<Args...>; | |
static constexpr auto number_of_parameters = sizeof...(Args); | |
}; | |
/** | |
* infer_function_traits: creates a `function_traits` type for a simple | |
* function (pointer) or functor (lambda/struct). Currently does not support | |
* class methods. | |
*/ | |
template <typename Functor> | |
struct infer_function_traits { | |
using type = function_traits< | |
c10::guts::detail::strip_class_t<decltype(&Functor::operator())>>; | |
}; | |
template <typename Result, typename... Args> | |
struct infer_function_traits<Result (*)(Args...)> { | |
using type = function_traits<Result(Args...)>; | |
}; | |
template <typename Result, typename... Args> | |
struct infer_function_traits<Result(Args...)> { | |
using type = function_traits<Result(Args...)>; | |
}; | |
template <typename T> | |
using infer_function_traits_t = typename infer_function_traits<T>::type; | |
/** | |
* make_function_traits: creates a `function_traits` type given a Return type | |
* and a typelist of Argument types | |
* | |
* Example: | |
* bool f(int, int); | |
* | |
* infer_function_traits_t<f> == make_function_traits_t<bool, | |
* typelist::typelist<int, int>> | |
*/ | |
template <typename Result, typename ArgList> | |
struct make_function_traits { | |
static_assert( | |
false_t<ArgList>::value, | |
"In guts::make_function_traits<Result, TypeList>, the ArgList argument must be typelist<...>."); | |
}; | |
template <typename Result, typename... Args> | |
struct make_function_traits<Result, typelist::typelist<Args...>> { | |
using type = function_traits<Result(Args...)>; | |
}; | |
template <typename Result, typename ArgList> | |
using make_function_traits_t = | |
typename make_function_traits<Result, ArgList>::type; | |
/** | |
* make_offset_index_sequence<Start, N> | |
* Like make_index_sequence<N>, but starting from Start instead of 0. | |
* | |
* Example: | |
* make_offset_index_sequence<10, 3> == std::index_sequence<10, 11, 12> | |
*/ | |
template <size_t Start, size_t N, size_t... Is> | |
struct make_offset_index_sequence_impl | |
: make_offset_index_sequence_impl<Start, N - 1, Start + N - 1, Is...> { | |
static_assert( | |
static_cast<int>(Start) >= 0, | |
"make_offset_index_sequence: Start < 0"); | |
static_assert(static_cast<int>(N) >= 0, "make_offset_index_sequence: N < 0"); | |
}; | |
template <size_t Start, size_t... Is> | |
struct make_offset_index_sequence_impl<Start, 0, Is...> { | |
typedef std::index_sequence<Is...> type; | |
}; | |
template <size_t Start, size_t N> | |
using make_offset_index_sequence = | |
typename make_offset_index_sequence_impl<Start, N>::type; | |
/** | |
* Use tuple_elements to extract a position-indexed subset of elements | |
* from the argument tuple into a result tuple. | |
* | |
* Example: | |
* std::tuple<int, const char*, double> t = std::make_tuple(0, "HEY", 2.0); | |
* std::tuple<int, double> result = tuple_elements(t, std::index_sequence<0, | |
* 2>()); | |
*/ | |
template <class Tuple, size_t... Is> | |
constexpr auto tuple_elements(Tuple t, std::index_sequence<Is...>) { | |
return std::tuple<std::tuple_element_t<Is, Tuple>...>(std::get<Is>(t)...); | |
} | |
/** | |
* Use tuple_take to extract the first or last n elements from the argument | |
* tuple into a result tuple. | |
* | |
* Example: | |
* std::tuple<int, const char*, double> t = std::make_tuple(0, "HEY", 2.0); | |
* std::tuple<int, const char*> first_two = tuple_take<decltype(t), 2>(t); | |
* std::tuple<const char*, double> last_two = tuple_take<decltype(t), -2>(t); | |
*/ | |
template <class Tuple, int N, class Enable = void> | |
struct TupleTake {}; | |
template <class Tuple, int N> | |
struct TupleTake<Tuple, N, std::enable_if_t<N >= 0, void>> { | |
static auto call(Tuple t) { | |
constexpr size_t size = std::tuple_size<Tuple>(); | |
static_assert(N <= size, "tuple_take: N > size"); | |
return tuple_elements(t, std::make_index_sequence<N>{}); | |
} | |
}; | |
template <class Tuple, int N> | |
struct TupleTake < Tuple, | |
N, std::enable_if_t<N<0, void>> { | |
static auto call(Tuple t) { | |
constexpr size_t size = std::tuple_size<Tuple>(); | |
static_assert(-N <= size, "tuple_take: -N > size"); | |
return tuple_elements(t, make_offset_index_sequence<size + N, -N>{}); | |
} | |
}; | |
template <class Tuple, int N> | |
auto tuple_take(Tuple t) { | |
return TupleTake<Tuple, N>::call(t); | |
} | |
/** | |
* Use tuple_slice to extract a contiguous subtuple from the argument. | |
* | |
* Example: | |
* std::tuple<int, const char*, double, bool> t = std::make_tuple(0, | |
* "HEY", 2.0, false); std::tuple<int, const char*> middle_two = | |
* tuple_slice<decltype(t), 1, 2>(t); | |
*/ | |
template <class Tuple, size_t Start, size_t N> | |
constexpr auto tuple_slice(Tuple t) { | |
constexpr size_t size = std::tuple_size<Tuple>(); | |
static_assert(Start + N <= size, "tuple_slice: Start + N > size"); | |
return tuple_elements(t, make_offset_index_sequence<Start, N>{}); | |
} | |
/** | |
* Use tuple_map to run a mapping function over a tuple to get a new tuple. | |
* | |
* Example 1: | |
* auto result = tuple_map(std::tuple<int32_t, int32_t, int32_t>(3, 4, 5), [] | |
* (int32_t a) -> int16_t {return a+1;}); | |
* // result == std::tuple<int16_t, int16_t, int16_t>(4, 5, 6) | |
* | |
* Example 2: | |
* struct Mapper { | |
* std::string operator()(int32_t a) const { | |
* return std::to_string(a); | |
* } | |
* int64_t operator()(const std::string& a) const { | |
* return atoi(a.c_str()); | |
* } | |
* }; | |
* auto result = tuple_map(std::tuple<int32_t, std::string>(3, "4"), | |
* Mapper()); | |
* // result == std::tuple<std::string, int64_t>("3", 4) | |
* | |
* Example 3: | |
* struct A final { | |
* int32_t func() { | |
* return 5; | |
* } | |
* }; | |
* struct B final { | |
* std::string func() { | |
* return "5"; | |
* } | |
* }; | |
* auto result = tuple_map(std::make_tuple(A(), B()), [] (auto a) { return | |
* a.func(); }); | |
* // result == std::tuple<int32_t, std::string>(5, "5"); | |
*/ | |
namespace detail { | |
template <class Mapper, class... Args, size_t... Indices> | |
auto tuple_map( | |
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) | |
std::tuple<Args...>&& tuple, | |
const Mapper& mapper, | |
std::index_sequence<Indices...>) { | |
return std::tuple<decltype(mapper(std::forward<Args>(std::get<Indices>( | |
tuple))))...>(mapper(std::forward<Args>(std::get<Indices>(tuple)))...); | |
} | |
} // namespace detail | |
template <class Mapper, class... Args> | |
auto tuple_map(std::tuple<Args...>&& tuple, const Mapper& mapper) { | |
return detail::tuple_map( | |
std::move(tuple), mapper, std::index_sequence_for<Args...>()); | |
} | |
} // namespace c10::guts | |