Commit 7d06adb4 authored by Pavel Aslanov's avatar Pavel Aslanov Committed by Facebook Github Bot

folly::overload and folly::variant_match

Summary: Pattern matching like functionality for boost::vairant. See unittests for usage examples.

Reviewed By: yfeldblum, ericniebler

Differential Revision: D4851133

fbshipit-source-id: cda7dc766dac5870bcc4ab1859de0e4e7f0a6599
parent 0ef8ce0d
...@@ -215,4 +215,25 @@ class DiscriminatedPtr { ...@@ -215,4 +215,25 @@ class DiscriminatedPtr {
uintptr_t data_; uintptr_t data_;
}; };
template <typename Visitor, typename... Args>
decltype(auto) apply_visitor(
Visitor&& visitor,
const DiscriminatedPtr<Args...>& variant) {
return variant.apply(std::forward<Visitor>(visitor));
}
template <typename Visitor, typename... Args>
decltype(auto) apply_visitor(
Visitor&& visitor,
DiscriminatedPtr<Args...>& variant) {
return variant.apply(std::forward<Visitor>(visitor));
}
template <typename Visitor, typename... Args>
decltype(auto) apply_visitor(
Visitor&& visitor,
DiscriminatedPtr<Args...>&& variant) {
return variant.apply(std::forward<Visitor>(visitor));
}
} // namespace folly } // namespace folly
...@@ -291,6 +291,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -291,6 +291,7 @@ nobase_follyinclude_HEADERS = \
MPMCPipeline.h \ MPMCPipeline.h \
MPMCQueue.h \ MPMCQueue.h \
Optional.h \ Optional.h \
Overload.h \
PackedSyncPtr.h \ PackedSyncPtr.h \
Padded.h \ Padded.h \
Partial.h \ Partial.h \
......
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <type_traits>
#include <utility>
/**
* folly implementation of `std::overload` like functionality
*
* Example:
* struct One {};
* struct Two {};
* boost::variant<One, Two> value;
*
* variant_match(value,
* [] (const One& one) { ... },
* [] (const Two& two) { ... });
*/
namespace folly {
namespace details {
template <typename...>
struct Overload;
template <typename Case, typename... Cases>
struct Overload<Case, Cases...> : Overload<Cases...>, Case {
Overload(Case c, Cases... cs)
: Overload<Cases...>(std::move(cs)...), Case(std::move(c)) {}
using Case::operator();
using Overload<Cases...>::operator();
};
template <typename Case>
struct Overload<Case> : Case {
explicit Overload(Case c) : Case(std::move(c)) {}
using Case::operator();
};
} // details
/*
* Combine multiple `Cases` in one function object
*/
template <typename... Cases>
decltype(auto) overload(Cases&&... cases) {
return details::Overload<typename std::decay<Cases>::type...>{
std::forward<Cases>(cases)...};
}
/*
* Match `Variant` with one of the `Cases`
*
* Note: you can also use `[] (const auto&) {...}` as default case
*
*/
template <typename Variant, typename... Cases>
decltype(auto) variant_match(Variant&& variant, Cases&&... cases) {
return apply_visitor(
overload(std::forward<Cases>(cases)...), std::forward<Variant>(variant));
}
} // folly
...@@ -86,6 +86,9 @@ TEST(DiscriminatedPtr, Apply) { ...@@ -86,6 +86,9 @@ TEST(DiscriminatedPtr, Apply) {
p.set(&foo); p.set(&foo);
EXPECT_EQ("Foo", p.apply(Visitor())); EXPECT_EQ("Foo", p.apply(Visitor()));
EXPECT_EQ("const Foo", static_cast<const Ptr&>(p).apply(Visitor())); EXPECT_EQ("const Foo", static_cast<const Ptr&>(p).apply(Visitor()));
EXPECT_EQ("Foo", apply_visitor(Visitor(), p));
EXPECT_EQ("const Foo", apply_visitor(Visitor(), static_cast<const Ptr&>(p)));
EXPECT_EQ("Foo", apply_visitor(Visitor(), std::move(p)));
p.clear(); p.clear();
EXPECT_THROW({p.apply(Visitor());}, std::invalid_argument); EXPECT_THROW({p.apply(Visitor());}, std::invalid_argument);
......
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <boost/variant.hpp>
#include <folly/DiscriminatedPtr.h>
#include <folly/Overload.h>
#include <folly/portability/GTest.h>
namespace folly {
namespace test {
struct One {
std::string toString() const {
return "One";
}
};
struct Two {
std::string toString() const {
return "Two";
}
};
using OneOrTwo = boost::variant<One, Two>;
TEST(Overload, BoostVariant) {
OneOrTwo one(One{});
OneOrTwo 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, DiscriminatedPtr) {
using V = DiscriminatedPtr<One, Two>;
One one_obj;
Two two_obj;
V one_ptr(&one_obj);
V two_ptr(&two_obj);
EXPECT_TRUE(variant_match(
one_ptr,
[](const One*) { return true; },
[](const Two*) { return false; }));
EXPECT_TRUE(variant_match(
two_ptr,
[](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_ptr), "One");
EXPECT_EQ(toString(two_ptr), "Two");
}
TEST(Overload, Pattern) {
OneOrTwo one(One{});
OneOrTwo two(Two{});
auto is_one_overload = overload(
[](const One&) { return true; }, [](const Two&) { return false; });
EXPECT_TRUE(boost::apply_visitor(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(boost::apply_visitor(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));
}
}
} // folly::test
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