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<> {
template <class 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
/// \cond
......@@ -41,6 +56,11 @@ template <class I1, class I2>
using Comparable = Conjunction<
std::is_same<std::decay_t<I1>, std::decay_t<I2>>,
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
/// \endcond
......@@ -49,9 +69,14 @@ template <
class I2,
std::enable_if_t<detail::Comparable<I1, I2>::value, int> = 0>
bool operator==(Poly<I1> const& _this, Poly<I2> const& that) {
return (poly_empty(_this) && poly_empty(that)) ||
(poly_type(_this) == poly_type(that) &&
::folly::poly_call<0, poly::IEqualityComparable>(_this, that));
if (poly_empty(_this) != 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::IEqualityComparable>(_this, that);
}
template <
......@@ -62,6 +87,45 @@ bool operator!=(Poly<I1> const& _this, Poly<I2> const& 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 {
/**
* A `Poly` interface for types that are move-only.
......
......@@ -57,6 +57,9 @@ struct Big {
friend bool operator!=(Big const& a, Big const& b) {
return !(a == b);
}
friend bool operator<(Big const& a, Big const& b) {
return a.value() < b.value();
}
static std::ptrdiff_t s_count;
};
std::ptrdiff_t Big::s_count = 0;
......@@ -92,6 +95,93 @@ TEST(Poly, SemiRegular) {
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) {
int i = 42;
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