Skip to content

Commit

Permalink
Add std::variant support for folly::variant_match.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mpark authored and facebook-github-bot committed Aug 22, 2020
1 parent e72f5c7 commit a5683e1
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 7 deletions.
23 changes: 22 additions & 1 deletion folly/Overload.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <type_traits>
#include <utility>

#include <folly/Traits.h>
#include <folly/functional/Invoke.h>

/**
* folly implementation of `std::overload` like functionality
*
Expand Down Expand Up @@ -64,15 +67,33 @@ decltype(auto) overload(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`
*
* 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>
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));
}

Expand Down
61 changes: 55 additions & 6 deletions folly/test/OverloadTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/

#include <folly/Overload.h>

#include <variant>

#include <boost/variant.hpp>
#include <folly/DiscriminatedPtr.h>
#include <folly/portability/GTest.h>
Expand All @@ -32,11 +35,29 @@ struct 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) {
OneOrTwo one(One{});
OneOrTwo two(Two{});
using V = boost::variant<One, Two>;
V one(One{});
V two(Two{});

EXPECT_TRUE(variant_match(
one, [](const One&) { return true; }, [](const Two&) { return false; }));
Expand Down Expand Up @@ -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<One, 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(
[](const One&) { return true; }, [](const Two&) { return false; });
Expand All @@ -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

0 comments on commit a5683e1

Please sign in to comment.