1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Support comparison operations on wrapper types -- e.g. |JS::Rooted<T>|,
9 * |JS::Handle<T>|, and so on -- against raw |T| values, against pointers or
10 * |nullptr| if the wrapper is a pointer wrapper, and against other wrappers
11 * around compatible types.
14 #ifndef js_ComparisonOperators_h
15 #define js_ComparisonOperators_h
17 #include <type_traits> // std::false_type, std::true_type, std::enable_if_t, std::is_pointer_v, std::remove_pointer_t
19 // To define |operator==| and |operator!=| for a wrapper class |W| (which may
20 // or may not be a template class) that contains a |T|:
22 // * Specialize |JS::detail::DefineComparisonOps| for |W|:
23 // - Make it inherit from |std::true_type|.
24 // - Include within your specialization a |static get(const W& v)| function
25 // that returns the value (which may be an lvalue reference) of the |T| in
27 // * If needed, add |using JS::detail::wrapper_comparison::operator==;| and
28 // |using JS::detail::wrapper_comparison::operator!=;| to the namespace
29 // directly containing |W| at the end of this header. (If you are not in
30 // SpiderMonkey code and have questionably decided to define your own
31 // wrapper class, add these to its namespace somewhere in your code.)
33 // The first step opts the wrapper class into comparison support and defines a
34 // generic means of extracting a comparable |T| out of an instance.
36 // The second step ensures that symmetric |operator==| and |operator!=| are
37 // exposed for the wrapper, accepting two wrappers or a wrapper and a suitable
40 // Failure to perform *both* steps will likely result in errors like
41 // 'invalid operands to binary expression' or 'no match for operator=='
42 // when comparing an instance of your wrapper.
48 // By default, comparison ops are not supported for types.
50 struct DefineComparisonOps
: std::false_type
{};
52 // Define functions for the core equality operations, that the actual operators
55 // Compare two wrapper types. Assumes both wrapper types support comparison
57 template <typename W
, typename OW
>
58 inline bool WrapperEqualsWrapper(const W
& wrapper
, const OW
& other
) {
59 return JS::detail::DefineComparisonOps
<W
>::get(wrapper
) ==
60 JS::detail::DefineComparisonOps
<OW
>::get(other
);
63 // Compare a wrapper against a value of its unwrapped element type (or against a
64 // value that implicitly converts to that unwrapped element type). Assumes its
65 // wrapper argument supports comparison operators.
67 inline bool WrapperEqualsUnwrapped(const W
& wrapper
,
68 const typename
W::ElementType
& value
) {
69 return JS::detail::DefineComparisonOps
<W
>::get(wrapper
) == value
;
72 // Compare a wrapper containing a pointer against a pointer to const element
73 // type. Assumes its wrapper argument supports comparison operators.
75 inline bool WrapperEqualsPointer(
77 const typename
std::remove_pointer_t
<typename
W::ElementType
>* ptr
) {
78 return JS::detail::DefineComparisonOps
<W
>::get(wrapper
) == ptr
;
81 // It is idiomatic C++ to define operators on user-defined types in the
82 // namespace of their operands' types (not at global scope, which isn't examined
83 // if at point of operator use another operator definition shadows the global
84 // definition). But our wrappers live in *multiple* namespaces (|namespace js|
85 // and |namespace JS| in SpiderMonkey), so we can't literally do that without
86 // defining ambiguous overloads.
88 // Instead, we define the operators *once* in a namespace containing nothing
89 // else at all. Then we |using| the operators into each namespace containing
90 // a wrapper type. |using| creates *aliases*, so two |using|s of the same
91 // operator contribute only one overload to overload resolution.
92 namespace wrapper_comparison
{
94 // Comparisons between potentially-differing wrappers.
95 template <typename W
, typename OW
>
96 inline typename
std::enable_if_t
<JS::detail::DefineComparisonOps
<W
>::value
&&
97 JS::detail::DefineComparisonOps
<OW
>::value
,
99 operator==(const W
& wrapper
, const OW
& other
) {
100 return JS::detail::WrapperEqualsWrapper(wrapper
, other
);
103 template <typename W
, typename OW
>
104 inline typename
std::enable_if_t
<JS::detail::DefineComparisonOps
<W
>::value
&&
105 JS::detail::DefineComparisonOps
<OW
>::value
,
107 operator!=(const W
& wrapper
, const OW
& other
) {
108 return !JS::detail::WrapperEqualsWrapper(wrapper
, other
);
111 // Comparisons between a wrapper and its unwrapped element type.
112 template <typename W
>
113 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
114 operator==(const W
& wrapper
, const typename
W::ElementType
& value
) {
115 return WrapperEqualsUnwrapped(wrapper
, value
);
118 template <typename W
>
119 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
120 operator!=(const W
& wrapper
, const typename
W::ElementType
& value
) {
121 return !WrapperEqualsUnwrapped(wrapper
, value
);
124 template <typename W
>
125 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
126 operator==(const typename
W::ElementType
& value
, const W
& wrapper
) {
127 return WrapperEqualsUnwrapped(wrapper
, value
);
130 template <typename W
>
131 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
132 operator!=(const typename
W::ElementType
& value
, const W
& wrapper
) {
133 return !WrapperEqualsUnwrapped(wrapper
, value
);
136 // For wrappers around a pointer type, comparisons between a wrapper object
137 // and a const element pointer.
138 template <typename W
>
139 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
&&
140 std::is_pointer_v
<typename
W::ElementType
>,
142 operator==(const W
& wrapper
,
143 const typename
std::remove_pointer_t
<typename
W::ElementType
>* ptr
) {
144 return WrapperEqualsPointer(wrapper
, ptr
);
147 template <typename W
>
148 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
&&
149 std::is_pointer_v
<typename
W::ElementType
>,
151 operator!=(const W
& wrapper
,
152 const typename
std::remove_pointer_t
<typename
W::ElementType
>* ptr
) {
153 return !WrapperEqualsPointer(wrapper
, ptr
);
156 template <typename W
>
157 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
&&
158 std::is_pointer_v
<typename
W::ElementType
>,
160 operator==(const typename
std::remove_pointer_t
<typename
W::ElementType
>* ptr
,
162 return WrapperEqualsPointer(wrapper
, ptr
);
165 template <typename W
>
166 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
&&
167 std::is_pointer_v
<typename
W::ElementType
>,
169 operator!=(const typename
std::remove_pointer_t
<typename
W::ElementType
>* ptr
,
171 return !WrapperEqualsPointer(wrapper
, ptr
);
174 // For wrappers around a pointer type, comparisons between a wrapper object
177 // These overloads are a workaround for gcc hazard build bugs. Per spec,
178 // |nullptr -> const T*| for the wrapper-pointer operators immediately above
179 // this is a standard conversion sequence (consisting of a single pointer
180 // conversion). Meanwhile, |nullptr -> T* const&| for the wrapper-element
181 // operators just above that, is a pointer conversion to |T*|, then an identity
182 // conversion of the |T* const| to a reference. The former conversion sequence
183 // is a proper subsequence of the latter, so it *should* be a better conversion
184 // sequence and thus should be the better overload. But gcc doesn't implement
185 // things this way, so we add overloads directly targeting |nullptr| as an exact
186 // match, preferred to either of those overloads.
188 // We should be able to remove these overloads when gcc hazard builds use modern
190 template <typename W
>
191 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
192 operator==(const W
& wrapper
, std::nullptr_t
) {
193 return WrapperEqualsUnwrapped(wrapper
, nullptr);
196 template <typename W
>
197 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
198 operator!=(const W
& wrapper
, std::nullptr_t
) {
199 return !WrapperEqualsUnwrapped(wrapper
, nullptr);
202 template <typename W
>
203 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
204 operator==(std::nullptr_t
, const W
& wrapper
) {
205 return WrapperEqualsUnwrapped(wrapper
, nullptr);
208 template <typename W
>
209 inline typename
std::enable_if_t
<DefineComparisonOps
<W
>::value
, bool>
210 operator!=(std::nullptr_t
, const W
& wrapper
) {
211 return !WrapperEqualsUnwrapped(wrapper
, nullptr);
214 } // namespace wrapper_comparison
216 } // namespace detail
220 // Expose wrapper-supporting |operator==| and |operator!=| in the namespaces of
221 // all SpiderMonkey's wrapper classes that support comparisons.
225 using JS::detail::wrapper_comparison::operator==;
226 using JS::detail::wrapper_comparison::operator!=;
232 using JS::detail::wrapper_comparison::operator==;
233 using JS::detail::wrapper_comparison::operator!=;
237 #endif // js_ComparisonOperators_h