blob: 302c5a8376c8d5649d50a18ddadc31ebd8cdafd2 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: libcpp-no-exceptions
// <algorithm>
// template <class _Compare> struct __debug_less
// __debug_less checks that a comparator actually provides a strict-weak ordering.
struct DebugException {};
#define _LIBCPP_DEBUG 0
#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : throw ::DebugException())
#include <algorithm>
#include <cassert>
template <int ID>
struct MyType {
int value;
explicit MyType(int xvalue = 0) : value(xvalue) {}
};
template <int ID1, int ID2>
bool operator<(MyType<ID1> const& LHS, MyType<ID2> const& RHS) {
return LHS.value < RHS.value;
}
struct CompareBase {
static int called;
static void reset() {
called = 0;
}
};
int CompareBase::called = 0;
template <class ValueType>
struct GoodComparator : public CompareBase {
bool operator()(ValueType const& lhs, ValueType const& rhs) const {
++CompareBase::called;
return lhs < rhs;
}
};
template <class ValueType>
struct BadComparator : public CompareBase {
bool operator()(ValueType const&, ValueType const&) const {
++CompareBase::called;
return true;
}
};
template <class T1, class T2>
struct TwoWayHomoComparator : public CompareBase {
bool operator()(T1 const& lhs, T2 const& rhs) const {
++CompareBase::called;
return lhs < rhs;
}
bool operator()(T2 const& lhs, T1 const& rhs) const {
++CompareBase::called;
return lhs < rhs;
}
};
template <class T1, class T2>
struct OneWayHomoComparator : public CompareBase {
bool operator()(T1 const& lhs, T2 const& rhs) const {
++CompareBase::called;
return lhs < rhs;
}
};
using std::__debug_less;
typedef MyType<0> MT0;
typedef MyType<1> MT1;
void test_passing() {
int& called = CompareBase::called;
called = 0;
MT0 one(1);
MT0 two(2);
MT1 three(3);
MT1 four(4);
{
typedef GoodComparator<MT0> C;
typedef __debug_less<C> D;
C c;
D d(c);
assert(d(one, two) == true);
assert(called == 2);
called = 0;
assert(d(one, one) == false);
assert(called == 1);
called = 0;
assert(d(two, one) == false);
assert(called == 1);
called = 0;
}
{
typedef TwoWayHomoComparator<MT0, MT1> C;
typedef __debug_less<C> D;
C c;
D d(c);
assert(d(one, three) == true);
assert(called == 2);
called = 0;
assert(d(three, one) == false);
assert(called == 1);
called = 0;
}
{
typedef OneWayHomoComparator<MT0, MT1> C;
typedef __debug_less<C> D;
C c;
D d(c);
assert(d(one, three) == true);
assert(called == 1);
called = 0;
}
}
void test_failing() {
int& called = CompareBase::called;
called = 0;
MT0 one(1);
MT0 two(2);
{
typedef BadComparator<MT0> C;
typedef __debug_less<C> D;
C c;
D d(c);
try {
d(one, two);
assert(false);
} catch (DebugException const&) {
}
assert(called == 2);
called = 0;
}
}
template <int>
struct Tag {
explicit Tag(int v) : value(v) {}
int value;
};
template <class = void>
struct FooImp {
explicit FooImp(int x) : x_(x) {}
int x_;
};
template <class T>
inline bool operator<(FooImp<T> const& x, Tag<0> y) {
return x.x_ < y.value;
}
template <class T>
inline bool operator<(Tag<0>, FooImp<T> const&) {
static_assert(sizeof(FooImp<T>) != sizeof(FooImp<T>), "should not be instantiated");
}
template <class T>
inline bool operator<(Tag<1> x, FooImp<T> const& y) {
return x.value < y.x_;
}
template <class T>
inline bool operator<(FooImp<T> const&, Tag<1>) {
static_assert(sizeof(FooImp<T>) != sizeof(FooImp<T>), "should not be instantiated");
}
typedef FooImp<> Foo;
// Test that we don't attempt to call the comparator with the arguments reversed
// for upper_bound and lower_bound since the comparator or type is not required
// to support it, nor does it require the range to have a strict weak ordering.
// See llvm.org/PR39458
void test_upper_and_lower_bound() {
Foo table[] = {Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)};
{
Foo* iter = std::lower_bound(std::begin(table), std::end(table), Tag<0>(3));
assert(iter == (table + 2));
}
{
Foo* iter = std::upper_bound(std::begin(table), std::end(table), Tag<1>(3));
assert(iter == (table + 3));
}
}
int main() {
test_passing();
test_failing();
test_upper_and_lower_bound();
}