Commit a8e02287 authored by Boris Burkov's avatar Boris Burkov Committed by Facebook Github Bot

add StrictlyOrderable interface

Summary:
I would like to store a folly::Poly I'm defining in an ordered
container (boost::containers::flat_set). While I could implement operator<
for exactly my type, it seems nicer to add it as another interface all
Poly users can reuse.

Reviewed By: ericniebler

Differential Revision: D7415861

fbshipit-source-id: 1912eaa009acea5c243db3e14fc0715a0829f251
parent 87305387
...@@ -33,6 +33,21 @@ struct IEqualityComparable : PolyExtends<> { ...@@ -33,6 +33,21 @@ struct IEqualityComparable : PolyExtends<> {
template <class T> template <class T>
using Members = FOLLY_POLY_MEMBERS(&isEqual_<T>); using Members = FOLLY_POLY_MEMBERS(&isEqual_<T>);
}; };
/**
* A `Poly` interface for types that are strictly orderable.
*/
struct IStrictlyOrderable : PolyExtends<> {
template <class T>
static auto isLess_(T const& _this, T const& that)
-> decltype(std::declval<bool (&)(bool)>()(_this < that)) {
return _this < that;
}
template <class T>
using Members = FOLLY_POLY_MEMBERS(&isLess_<T>);
};
} // namespace poly } // namespace poly
/// \cond /// \cond
...@@ -41,6 +56,11 @@ template <class I1, class I2> ...@@ -41,6 +56,11 @@ template <class I1, class I2>
using Comparable = Conjunction< using Comparable = Conjunction<
std::is_same<std::decay_t<I1>, std::decay_t<I2>>, std::is_same<std::decay_t<I1>, std::decay_t<I2>>,
std::is_base_of<poly::IEqualityComparable, std::decay_t<I1>>>; std::is_base_of<poly::IEqualityComparable, std::decay_t<I1>>>;
template <class I1, class I2>
using Orderable = Conjunction<
std::is_same<std::decay_t<I1>, std::decay_t<I2>>,
std::is_base_of<poly::IStrictlyOrderable, std::decay_t<I1>>>;
} // namespace detail } // namespace detail
/// \endcond /// \endcond
...@@ -49,9 +69,14 @@ template < ...@@ -49,9 +69,14 @@ template <
class I2, class I2,
std::enable_if_t<detail::Comparable<I1, I2>::value, int> = 0> std::enable_if_t<detail::Comparable<I1, I2>::value, int> = 0>
bool operator==(Poly<I1> const& _this, Poly<I2> const& that) { bool operator==(Poly<I1> const& _this, Poly<I2> const& that) {
return (poly_empty(_this) && poly_empty(that)) || if (poly_empty(_this) != poly_empty(that)) {
(poly_type(_this) == poly_type(that) && return false;
::folly::poly_call<0, poly::IEqualityComparable>(_this, that)); } else if (poly_empty(_this)) {
return true;
} else if (poly_type(_this) != poly_type(that)) {
throw BadPolyCast();
}
return ::folly::poly_call<0, poly::IEqualityComparable>(_this, that);
} }
template < template <
...@@ -62,6 +87,45 @@ bool operator!=(Poly<I1> const& _this, Poly<I2> const& that) { ...@@ -62,6 +87,45 @@ bool operator!=(Poly<I1> const& _this, Poly<I2> const& that) {
return !(_this == that); return !(_this == that);
} }
template <
class I1,
class I2,
std::enable_if_t<detail::Orderable<I1, I2>::value, int> = 0>
bool operator<(Poly<I1> const& _this, Poly<I2> const& that) {
if (poly_empty(that)) {
return false;
} else if (poly_empty(_this)) {
return true;
} else if (poly_type(_this) != poly_type(that)) {
throw BadPolyCast{};
}
return ::folly::poly_call<0, poly::IStrictlyOrderable>(_this, that);
}
template <
class I1,
class I2,
std::enable_if_t<detail::Orderable<I1, I2>::value, int> = 0>
bool operator>(Poly<I1> const& _this, Poly<I2> const& that) {
return that < _this;
}
template <
class I1,
class I2,
std::enable_if_t<detail::Orderable<I1, I2>::value, int> = 0>
bool operator<=(Poly<I1> const& _this, Poly<I2> const& that) {
return !(that < _this);
}
template <
class I1,
class I2,
std::enable_if_t<detail::Orderable<I1, I2>::value, int> = 0>
bool operator>=(Poly<I1> const& _this, Poly<I2> const& that) {
return !(_this < that);
}
namespace poly { namespace poly {
/** /**
* A `Poly` interface for types that are move-only. * A `Poly` interface for types that are move-only.
......
...@@ -57,6 +57,9 @@ struct Big { ...@@ -57,6 +57,9 @@ struct Big {
friend bool operator!=(Big const& a, Big const& b) { friend bool operator!=(Big const& a, Big const& b) {
return !(a == b); return !(a == b);
} }
friend bool operator<(Big const& a, Big const& b) {
return a.value() < b.value();
}
static std::ptrdiff_t s_count; static std::ptrdiff_t s_count;
}; };
std::ptrdiff_t Big::s_count = 0; std::ptrdiff_t Big::s_count = 0;
...@@ -92,6 +95,93 @@ TEST(Poly, SemiRegular) { ...@@ -92,6 +95,93 @@ TEST(Poly, SemiRegular) {
EXPECT_EQ(0, Big::s_count); EXPECT_EQ(0, Big::s_count);
} }
TEST(Poly, EqualityComparable) {
{
Poly<IEqualityComparable> p = 42;
Poly<IEqualityComparable> q = 42;
EXPECT_TRUE(p == q);
EXPECT_TRUE(q == p);
EXPECT_FALSE(p != q);
EXPECT_FALSE(q != p);
p = 43;
EXPECT_FALSE(p == q);
EXPECT_FALSE(q == p);
EXPECT_TRUE(p != q);
EXPECT_TRUE(q != p);
}
{
// empty not equal
Poly<IEqualityComparable> p;
Poly<IEqualityComparable> q = 42;
EXPECT_FALSE(p == q);
EXPECT_FALSE(q == p);
}
{
// empty equal
Poly<IEqualityComparable> p;
Poly<IEqualityComparable> q;
EXPECT_TRUE(p == q);
EXPECT_TRUE(q == p);
}
{
// mismatched types throws
Poly<IEqualityComparable> p = 4.2;
Poly<IEqualityComparable> q = 42;
bool b;
EXPECT_THROW(b = (q == p), BadPolyCast);
}
}
TEST(Poly, StrictlyOrderable) {
{
// A small object, storable in-situ:
Poly<IStrictlyOrderable> p = 42;
Poly<IStrictlyOrderable> q = 43;
EXPECT_TRUE(p < q);
EXPECT_TRUE(p <= q);
EXPECT_FALSE(p > q);
EXPECT_FALSE(p >= q);
EXPECT_TRUE(q > p);
EXPECT_TRUE(q >= p);
EXPECT_FALSE(q < p);
EXPECT_FALSE(q <= p);
}
{
// A big object, stored on the heap:
Poly<IStrictlyOrderable> p = Big(42);
Poly<IStrictlyOrderable> q = Big(43);
EXPECT_TRUE(p < q);
}
{
// if equal, no one is bigger
Poly<IStrictlyOrderable> p = 42;
Poly<IStrictlyOrderable> q = 42;
EXPECT_FALSE(p < q);
EXPECT_TRUE(p <= q);
EXPECT_FALSE(p > q);
EXPECT_TRUE(p >= q);
EXPECT_FALSE(q < p);
EXPECT_TRUE(q <= p);
EXPECT_FALSE(q > p);
EXPECT_TRUE(q >= p);
}
{
// empty is always smaller
Poly<IStrictlyOrderable> p;
Poly<IStrictlyOrderable> q = 42;
EXPECT_TRUE(p < q);
EXPECT_FALSE(q < p);
}
{
// mismatched types throws
Poly<IStrictlyOrderable> p = 4.2;
Poly<IStrictlyOrderable> q = 42;
bool b;
EXPECT_THROW(b = (p < q), BadPolyCast);
EXPECT_THROW(b = (q < p), BadPolyCast);
}
}
TEST(Poly, SemiRegularReference) { TEST(Poly, SemiRegularReference) {
int i = 42; int i = 42;
Poly<ISemiRegular&> p = i; Poly<ISemiRegular&> p = i;
......
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