Commit a5683e13 authored by Michael Park's avatar Michael Park Committed by Facebook GitHub Bot

Add `std::variant` support for `folly::variant_match`.

Summary: This patch adds support for `std::variant` to `folly::variant_match` but without including `<variant>`.

Reviewed By: yfeldblum

Differential Revision: D23196572

fbshipit-source-id: 9d5b519958b04c13cc024d393247c73cec00ac67
parent e72f5c7b
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <folly/Traits.h>
#include <folly/functional/Invoke.h>
/** /**
* folly implementation of `std::overload` like functionality * folly implementation of `std::overload` like functionality
* *
...@@ -64,15 +67,33 @@ decltype(auto) overload(Cases&&... cases) { ...@@ -64,15 +67,33 @@ decltype(auto) overload(Cases&&... cases) {
std::forward<Cases>(cases)...}; std::forward<Cases>(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` * Match `Variant` with one of the `Cases`
* *
* Note: you can also use `[] (const auto&) {...}` as default case * 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 <typename Variant, typename... Cases> template <typename Variant, typename... Cases>
decltype(auto) variant_match(Variant&& variant, Cases&&... cases) { decltype(auto) variant_match(Variant&& variant, Cases&&... cases) {
return apply_visitor( using invoker = std::conditional_t<
folly::Conjunction<
is_invocable<overload_detail::valueless_by_exception, Variant>,
is_invocable<
overload_detail::visit,
decltype(overload(std::forward<Cases>(cases)...)),
Variant>>::value,
overload_detail::visit,
overload_detail::apply_visitor>;
return invoker{}(
overload(std::forward<Cases>(cases)...), std::forward<Variant>(variant)); overload(std::forward<Cases>(cases)...), std::forward<Variant>(variant));
} }
......
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
*/ */
#include <folly/Overload.h> #include <folly/Overload.h>
#include <variant>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <folly/DiscriminatedPtr.h> #include <folly/DiscriminatedPtr.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
...@@ -32,11 +35,29 @@ struct Two { ...@@ -32,11 +35,29 @@ struct Two {
return "Two"; return "Two";
} }
}; };
using OneOrTwo = boost::variant<One, Two>;
TEST(Overload, StdVariant) {
using V = std::variant<One, Two>;
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) { TEST(Overload, BoostVariant) {
OneOrTwo one(One{}); using V = boost::variant<One, Two>;
OneOrTwo two(Two{}); V one(One{});
V two(Two{});
EXPECT_TRUE(variant_match( EXPECT_TRUE(variant_match(
one, [](const One&) { return true; }, [](const Two&) { return false; })); one, [](const One&) { return true; }, [](const Two&) { return false; }));
...@@ -75,9 +96,36 @@ TEST(Overload, DiscriminatedPtr) { ...@@ -75,9 +96,36 @@ TEST(Overload, DiscriminatedPtr) {
EXPECT_EQ(toString(two_ptr), "Two"); EXPECT_EQ(toString(two_ptr), "Two");
} }
TEST(Overload, Pattern) { TEST(Overload, StdPattern) {
OneOrTwo one(One{}); using V = std::variant<One, Two>;
OneOrTwo two(Two{}); 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<const decltype(is_one_overload)&>(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<One, Two>;
V one(One{});
V two(Two{});
auto is_one_overload = overload( auto is_one_overload = overload(
[](const One&) { return true; }, [](const Two&) { return false; }); [](const One&) { return true; }, [](const Two&) { return false; });
...@@ -99,5 +147,6 @@ TEST(Overload, Pattern) { ...@@ -99,5 +147,6 @@ TEST(Overload, Pattern) {
EXPECT_FALSE(variant_match(two, is_one_copy)); EXPECT_FALSE(variant_match(two, is_one_copy));
EXPECT_FALSE(variant_match(two, is_one_const_copy)); EXPECT_FALSE(variant_match(two, is_one_const_copy));
} }
} // namespace test } // namespace test
} // namespace folly } // namespace folly
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