Commit 39e1a6e9 authored by Aaryaman Sagar's avatar Aaryaman Sagar Committed by Facebook Github Bot

Add analogue of invoke_ family of traits for apply()

Summary:
Similar to the invoke family of traits
https://en.cppreference.com/w/cpp/types/is_invocable, this introduces the same
for the case where arguments are wrapped in a tuple

Reviewed By: yfeldblum

Differential Revision: D8626830

fbshipit-source-id: 145eb71c7bb142ff07ebad716572b5e8340d2c9c
parent a71bd7f2
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <folly/Traits.h>
#include <folly/Utility.h> #include <folly/Utility.h>
#include <folly/functional/Invoke.h> #include <folly/functional/Invoke.h>
...@@ -29,7 +30,10 @@ namespace folly { ...@@ -29,7 +30,10 @@ namespace folly {
namespace detail { namespace detail {
template <class F, class T, std::size_t... I> template <class F, class T, std::size_t... I>
constexpr decltype(auto) applyImpl(F&& f, T&& t, folly::index_sequence<I...>) { constexpr auto applyImpl(F&& f, T&& t, folly::index_sequence<I...>) noexcept(
is_nothrow_invocable<F&&, decltype(std::get<I>(std::declval<T>()))...>::
value)
-> invoke_result_t<F&&, decltype(std::get<I>(std::declval<T>()))...> {
return invoke(std::forward<F>(f), std::get<I>(std::forward<T>(t))...); return invoke(std::forward<F>(f), std::get<I>(std::forward<T>(t))...);
} }
} // namespace detail } // namespace detail
...@@ -53,6 +57,51 @@ template <typename Tuple> ...@@ -53,6 +57,51 @@ template <typename Tuple>
using index_sequence_for_tuple = using index_sequence_for_tuple =
make_index_sequence<std::tuple_size<Tuple>::value>; make_index_sequence<std::tuple_size<Tuple>::value>;
/**
* Mimic the invoke suite of traits for tuple based apply invocation
*/
template <typename F, typename Tuple>
using apply_result_t = decltype(detail::applyImpl(
std::declval<F>(),
std::declval<Tuple>(),
index_sequence_for_tuple<std::remove_reference_t<Tuple>>{}));
template <typename F, typename Tuple, typename = void_t<>>
class apply_result {};
template <typename F, typename Tuple>
class apply_result<F, Tuple, void_t<apply_result_t<F, Tuple>>> {
using type = apply_result_t<F, Tuple>;
};
template <typename F, typename Tuple, typename = void_t<>>
struct is_applicable : std::false_type {};
template <typename F, typename Tuple>
struct is_applicable<F, Tuple, void_t<apply_result_t<F, Tuple>>>
: std::true_type {};
template <typename R, typename F, typename Tuple, typename = void_t<>>
struct is_applicable_r : std::false_type {};
template <typename R, typename F, typename Tuple>
struct is_applicable_r<R, F, Tuple, void_t<apply_result_t<F, Tuple>>>
: std::is_convertible<apply_result_t<F, Tuple>, R> {};
template <typename F, typename Tuple, typename = void_t<>>
struct is_nothrow_applicable : std::false_type {};
template <typename F, typename Tuple>
struct is_nothrow_applicable<F, Tuple, void_t<apply_result_t<F, Tuple>>>
: bool_constant<noexcept(detail::applyImpl(
std::declval<F>(),
std::declval<Tuple>(),
index_sequence_for_tuple<std::remove_reference_t<Tuple>>{}))> {};
template <typename R, typename F, typename Tuple, typename = void_t<>>
struct is_nothrow_applicable_r : std::false_type {};
template <typename R, typename F, typename Tuple>
struct is_nothrow_applicable_r<R, F, Tuple, void_t<apply_result_t<F, Tuple>>>
: folly::Conjunction<
std::is_convertible<apply_result_t<F, Tuple>, R>,
is_nothrow_applicable<F, Tuple>> {};
namespace detail { namespace detail {
namespace apply_tuple { namespace apply_tuple {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <iostream> #include <iostream>
#include <folly/Overload.h>
#include <folly/functional/ApplyTuple.h> #include <folly/functional/ApplyTuple.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
...@@ -437,3 +438,116 @@ TEST(MakeIndexSequenceFromTuple, Basic) { ...@@ -437,3 +438,116 @@ TEST(MakeIndexSequenceFromTuple, Basic) {
index_sequence_for_tuple<const TwoElementTuple>, index_sequence_for_tuple<const TwoElementTuple>,
index_sequence<0>>::value)); index_sequence<0>>::value));
} }
TEST(ApplyResult, Basic) {
{
auto f = [](auto) -> int { return {}; };
EXPECT_TRUE((std::is_same<
folly::apply_result_t<decltype(f), std::tuple<int>>,
int>{}));
}
{
auto f = folly::overload(
[](int) {},
[](double) -> double { return {}; },
[](int, int) -> int { return {}; });
EXPECT_TRUE((std::is_same<
folly::apply_result_t<decltype(f), std::tuple<int>>,
void>::value));
EXPECT_TRUE((std::is_same<
folly::apply_result_t<decltype(f), std::tuple<double>>,
double>::value));
EXPECT_TRUE((std::is_same<
folly::apply_result_t<decltype(f), std::tuple<int, int>>,
int>::value));
}
}
TEST(IsApplicable, Basic) {
{
auto f = [] {};
EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<>>::value));
EXPECT_FALSE((folly::is_applicable<decltype(f), std::tuple<int>>::value));
}
{
auto f = folly::overload([](int) {}, [](double) -> double { return {}; });
EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<double>>::value));
EXPECT_TRUE((folly::is_applicable<decltype(f), std::tuple<int>>::value));
EXPECT_FALSE((folly::is_applicable<decltype(f), std::tuple<>>::value));
EXPECT_FALSE(
(folly::is_applicable<decltype(f), std::tuple<int, double>>::value));
}
}
TEST(IsNothrowApplicable, Basic) {
{
auto f = []() noexcept {};
EXPECT_TRUE((folly::is_nothrow_applicable<decltype(f), std::tuple<>>{}));
EXPECT_FALSE(
(folly::is_nothrow_applicable<decltype(f), std::tuple<int>>{}));
}
{
auto f = folly::overload([](int) noexcept {}, [](double) -> double {
return {};
});
EXPECT_FALSE(
(folly::is_nothrow_applicable<decltype(f), std::tuple<double>>{}));
EXPECT_TRUE((folly::is_nothrow_applicable<decltype(f), std::tuple<int>>{}));
EXPECT_FALSE((folly::is_nothrow_applicable<decltype(f), std::tuple<>>{}));
EXPECT_FALSE(
(folly::is_nothrow_applicable<decltype(f), std::tuple<int, double>>::
value));
}
}
TEST(IsApplicableR, Basic) {
{
auto f = []() -> int { return {}; };
EXPECT_TRUE((folly::is_applicable_r<double, decltype(f), std::tuple<>>{}));
EXPECT_FALSE(
(folly::is_applicable_r<double, decltype(f), std::tuple<int>>{}));
}
{
auto f = folly::overload([](int) noexcept {}, [](double) -> double {
return {};
});
EXPECT_TRUE(
(folly::is_applicable_r<float, decltype(f), std::tuple<double>>{}));
EXPECT_TRUE((folly::is_applicable_r<void, decltype(f), std::tuple<int>>{}));
EXPECT_FALSE((folly::is_applicable_r<void, decltype(f), std::tuple<>>{}));
EXPECT_FALSE(
(folly::is_applicable_r<double, decltype(f), std::tuple<int, double>>::
value));
}
}
TEST(IsNothrowApplicableR, Basic) {
{
auto f = []() noexcept->int {
return {};
};
EXPECT_TRUE(
(folly::is_nothrow_applicable_r<double, decltype(f), std::tuple<>>{}));
EXPECT_FALSE(
(folly::
is_nothrow_applicable_r<double, decltype(f), std::tuple<int>>{}));
}
{
auto f = folly::overload([](int) noexcept {}, [](double) -> double {
return {};
});
EXPECT_FALSE((
folly::
is_nothrow_applicable_r<float, decltype(f), std::tuple<double>>{}));
EXPECT_TRUE(
(folly::is_nothrow_applicable_r<void, decltype(f), std::tuple<int>>{}));
EXPECT_FALSE(
(folly::is_nothrow_applicable_r<void, decltype(f), std::tuple<>>{}));
EXPECT_FALSE((folly::is_nothrow_applicable_r<
double,
decltype(f),
std::tuple<int, double>>::value));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment