From a5683e130c2fc7ca2e20628414cc3eb490c52aea Mon Sep 17 00:00:00 2001 From: Michael Park Date: Sat, 22 Aug 2020 01:42:46 -0700 Subject: [PATCH] Add `std::variant` support for `folly::variant_match`. Summary: This patch adds support for `std::variant` to `folly::variant_match` but without including ``. Reviewed By: yfeldblum Differential Revision: D23196572 fbshipit-source-id: 9d5b519958b04c13cc024d393247c73cec00ac67 --- folly/Overload.h | 23 +++++++++++++- folly/test/OverloadTest.cpp | 61 +++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/folly/Overload.h b/folly/Overload.h index 5b3e0b2dd2a..4e6e8ae19e0 100644 --- a/folly/Overload.h +++ b/folly/Overload.h @@ -19,6 +19,9 @@ #include #include +#include +#include + /** * folly implementation of `std::overload` like functionality * @@ -64,15 +67,33 @@ decltype(auto) overload(Cases&&... cases) { std::forward(cases)...}; } +namespace overload_detail { +FOLLY_CREATE_MEMBER_INVOKER(valueless_by_exception, valueless_by_exception); +FOLLY_CREATE_FREE_INVOKER(visit, visit); +FOLLY_CREATE_FREE_INVOKER(apply_visitor, apply_visitor); +} // namespace overload_detail + /* * Match `Variant` with one of the `Cases` * * Note: you can also use `[] (const auto&) {...}` as default case * + * Selects `visit` if `v.valueless_by_exception()` available and the call to + * `visit` is valid (e.g. `std::variant`). Otherwise, selects `apply_visitor` + * (e.g. `boost::variant`, `folly::DiscriminatedPtr`). */ template decltype(auto) variant_match(Variant&& variant, Cases&&... cases) { - return apply_visitor( + using invoker = std::conditional_t< + folly::Conjunction< + is_invocable, + is_invocable< + overload_detail::visit, + decltype(overload(std::forward(cases)...)), + Variant>>::value, + overload_detail::visit, + overload_detail::apply_visitor>; + return invoker{}( overload(std::forward(cases)...), std::forward(variant)); } diff --git a/folly/test/OverloadTest.cpp b/folly/test/OverloadTest.cpp index 11169f5078e..0b79ab40af8 100644 --- a/folly/test/OverloadTest.cpp +++ b/folly/test/OverloadTest.cpp @@ -15,6 +15,9 @@ */ #include + +#include + #include #include #include @@ -32,11 +35,29 @@ struct Two { return "Two"; } }; -using OneOrTwo = boost::variant; + +TEST(Overload, StdVariant) { + using V = std::variant; + V one(One{}); + V two(Two{}); + + EXPECT_TRUE(variant_match( + one, [](const One&) { return true; }, [](const Two&) { return false; })); + EXPECT_TRUE(variant_match( + two, [](const One&) { return false; }, [](const Two&) { return true; })); + + auto toString = [](const auto& variant) { + return variant_match( + variant, [](const auto& value) { return value.toString(); }); + }; + EXPECT_EQ(toString(one), "One"); + EXPECT_EQ(toString(two), "Two"); +} TEST(Overload, BoostVariant) { - OneOrTwo one(One{}); - OneOrTwo two(Two{}); + using V = boost::variant; + V one(One{}); + V two(Two{}); EXPECT_TRUE(variant_match( one, [](const One&) { return true; }, [](const Two&) { return false; })); @@ -75,9 +96,36 @@ TEST(Overload, DiscriminatedPtr) { EXPECT_EQ(toString(two_ptr), "Two"); } -TEST(Overload, Pattern) { - OneOrTwo one(One{}); - OneOrTwo two(Two{}); +TEST(Overload, StdPattern) { + using V = std::variant; + V one(One{}); + V two(Two{}); + + auto is_one_overload = overload( + [](const One&) { return true; }, [](const Two&) { return false; }); + EXPECT_TRUE(std::visit(is_one_overload, one)); + EXPECT_TRUE(variant_match(one, is_one_overload)); + EXPECT_FALSE(variant_match(two, is_one_overload)); + + auto is_two_overload = overload( + [](const One&) { return false; }, [](const Two&) { return true; }); + EXPECT_TRUE(std::visit(is_two_overload, two)); + EXPECT_FALSE(variant_match(one, is_two_overload)); + EXPECT_TRUE(variant_match(two, is_two_overload)); + + auto is_one_copy = overload(is_one_overload); + auto is_one_const_copy = + overload(static_cast(is_one_overload)); + EXPECT_TRUE(variant_match(one, is_one_copy)); + EXPECT_TRUE(variant_match(one, is_one_const_copy)); + EXPECT_FALSE(variant_match(two, is_one_copy)); + EXPECT_FALSE(variant_match(two, is_one_const_copy)); +} + +TEST(Overload, BoostPattern) { + using V = boost::variant; + V one(One{}); + V two(Two{}); auto is_one_overload = overload( [](const One&) { return true; }, [](const Two&) { return false; }); @@ -99,5 +147,6 @@ TEST(Overload, Pattern) { EXPECT_FALSE(variant_match(two, is_one_copy)); EXPECT_FALSE(variant_match(two, is_one_const_copy)); } + } // namespace test } // namespace folly