forked from RobotLocomotion/drake
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsorted_pair.h
213 lines (180 loc) · 6.65 KB
/
sorted_pair.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#pragma once
#include <algorithm>
#include <cstddef>
#include <ostream>
#include <type_traits>
#include <utility>
#include "drake/common/hash.h"
#include "drake/common/is_less_than_comparable.h"
/// @file
/// Provides drake::MakeSortedPair and drake::SortedPair for storing two
/// values of a certain type in sorted order.
namespace drake {
/// This class is similar to the std::pair class. However, this class uses a
/// pair of homogeneous types (std::pair can use heterogeneous types) and sorts
/// the first and second values such that the first value is less than or equal
/// to the second one). Note that the sort is a stable one. Thus the SortedPair
/// class is able to be used to generate keys (e.g., for std::map, etc.) from
/// pairs of objects.
///
/// The availability of construction and assignment operations (i.e., default
/// constructor, copy constructor, copy assignment, move constructor, move
/// assignment) is the same as whatever T provides . All comparison operations
/// (including equality, etc.) are always available.
///
/// @tparam T A template type that provides `operator<`.
template <class T>
struct SortedPair {
static_assert(is_less_than_comparable<T>::value, "SortedPair can only be used"
" with types that can be compared using the less-than operator"
" (operator<).");
/// The default constructor creates `first()` and `second()` using T's default
/// constructor, iff T has a default constructor. Otherwise, this constructor
/// is not available.
#ifndef DRAKE_DOXYGEN_CXX
template <typename T1 = T,
typename std::enable_if_t<std::is_constructible_v<T1>, bool> = true>
#endif
SortedPair() {}
/// Rvalue reference constructor, permits constructing with std::unique_ptr
/// types, for example.
SortedPair(T&& a, T&& b) {
if (b < a) {
first_ = std::move(b);
second_ = std::move(a);
} else {
first_ = std::move(a);
second_ = std::move(b);
}
}
/// Constructs a %SortedPair from two objects.
SortedPair(const T& a, const T& b) : first_(a), second_(b) {
if (second_ < first_)
std::swap(first_, second_);
}
/// Type-converting copy constructor.
template <class U>
SortedPair(SortedPair<U>&& u) : first_{std::forward<T>(u.first())},
second_{std::forward<T>(u.second())} {}
// N.B. We leave all of the copy/move/assign operations implicitly declared,
// so that iff T provides that operation, then we will also provide it. Do
// not declare any of those operations nor a destructor here, or else the
// implicitly declared functions might not longer be implicitly declared.
/// Resets the stored objects.
template <class U>
void set(U&& a, U&& b) {
first_ = std::forward<U>(a);
second_ = std::forward<U>(b);
if (second_ < first_)
std::swap(first_, second_);
}
/// Gets the first (according to `operator<`) of the objects.
const T& first() const { return first_; }
/// Gets the second (according to `operator<`) of the objects.
const T& second() const { return second_; }
/// Swaps `this` and `t`.
void Swap(drake::SortedPair<T>& t) {
std::swap(t.first_, first_);
std::swap(t.second_, second_);
}
/// Implements the @ref hash_append concept.
template <class HashAlgorithm>
friend void hash_append(HashAlgorithm& hasher, const SortedPair& p) noexcept {
using drake::hash_append;
hash_append(hasher, p.first_);
hash_append(hasher, p.second_);
}
/// @name Support for using SortedPair in structured bindings.
//@{
template<std::size_t Index>
std::tuple_element_t<Index, SortedPair<T>>& get() {
if constexpr (Index == 0) return first_;
if constexpr (Index == 1) return second_;
}
template<std::size_t Index>
const std::tuple_element_t<Index, SortedPair<T>>& get() const {
if constexpr (Index == 0) return first_;
if constexpr (Index == 1) return second_;
}
//@}
private:
T first_{}; // The first of the two objects, according to operator<.
T second_{}; // The second of the two objects, according to operator<.
};
/// Support writing a SortedPair to a stream (conditional on the support of
/// writing the underlying type T to a stream).
template <typename T>
inline std::ostream& operator<<(std::ostream& out, const SortedPair<T>& pair) {
out << "(" << pair.first() << ", " << pair.second() << ")";
return out;
}
/// Two pairs of the same type are equal iff their members are equal after
/// sorting.
template <class T>
inline bool operator==(const SortedPair<T>& x, const SortedPair<T>& y) {
return !(x < y) && !(y < x);
}
/// Compares two pairs using lexicographic ordering.
template <class T>
inline bool operator<(const SortedPair<T>& x, const SortedPair<T>& y) {
return std::tie(x.first(), x.second()) < std::tie(y.first(), y.second());
}
/// Determine whether two SortedPair objects are not equal using `operator==`.
template <class T>
inline bool operator!=(
const SortedPair<T>& x, const SortedPair<T>& y) {
return !(x == y);
}
/// Determines whether `x > y` using `operator<`.
template <class T>
inline bool operator>(const SortedPair<T>& x, const SortedPair<T>& y) {
return y < x;
}
/// Determines whether `x <= y` using `operator<`.
template <class T>
inline bool operator<=(const SortedPair<T>& x, const SortedPair<T>& y) {
return !(y < x);
}
/// Determines whether `x >= y` using `operator<`.
template <class T>
inline bool
operator>=(const SortedPair<T>& x, const SortedPair<T>& y) {
return !(x < y);
}
/// @brief A convenience wrapper for creating a sorted pair from two objects.
/// @param x The first_ object.
/// @param y The second_ object.
/// @return A newly-constructed SortedPair object.
template <class T>
inline constexpr SortedPair<typename std::decay<T>::type>
MakeSortedPair(T&& x, T&& y) {
return SortedPair<
typename std::decay<T>::type>(std::forward<T>(x), std::forward<T>(y));
}
} // namespace drake
namespace std {
/// Implements std::swap().
template <class T>
void swap(drake::SortedPair<T>& t, drake::SortedPair<T>& u) {
t.Swap(u);
}
/// Provides std::hash<SortedPair<T>>.
template <class T>
struct hash<drake::SortedPair<T>>
: public drake::DefaultHash {};
#if defined(__GLIBCXX__)
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/unordered_associative.html
template <class T>
struct __is_fast_hash<hash<drake::SortedPair<T>>> : std::false_type {};
#endif
/// Support using `SortedPair<T>` in structured bindings. E.g.,
///
/// SortedPair<Foo> pair(Foo(1), Foo(2));
/// const auto& [a, b] = pair;
template <typename T>
struct tuple_size<drake::SortedPair<T>> : std::integral_constant<size_t, 2> {};
template <size_t Index, typename T>
struct tuple_element<Index, drake::SortedPair<T>> {
using type = T;
};
} // namespace std