Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / public / ComparisonOperators.h
blobc7f03fa4cac74bc50eed4a149e9e97da02051d42
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/. */
7 /*
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
26 // |W|.
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
38 // raw value.
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.
44 namespace JS {
46 namespace detail {
48 // By default, comparison ops are not supported for types.
49 template <typename T>
50 struct DefineComparisonOps : std::false_type {};
52 // Define functions for the core equality operations, that the actual operators
53 // can all invoke.
55 // Compare two wrapper types. Assumes both wrapper types support comparison
56 // operators.
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.
66 template <typename W>
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.
74 template <typename W>
75 inline bool WrapperEqualsPointer(
76 const W& wrapper,
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,
98 bool>
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,
106 bool>
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>,
141 bool>
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>,
150 bool>
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>,
159 bool>
160 operator==(const typename std::remove_pointer_t<typename W::ElementType>* ptr,
161 const W& wrapper) {
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>,
168 bool>
169 operator!=(const typename std::remove_pointer_t<typename W::ElementType>* ptr,
170 const W& wrapper) {
171 return !WrapperEqualsPointer(wrapper, ptr);
174 // For wrappers around a pointer type, comparisons between a wrapper object
175 // and |nullptr|.
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
189 // clang.
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
218 } // namespace JS
220 // Expose wrapper-supporting |operator==| and |operator!=| in the namespaces of
221 // all SpiderMonkey's wrapper classes that support comparisons.
223 namespace JS {
225 using JS::detail::wrapper_comparison::operator==;
226 using JS::detail::wrapper_comparison::operator!=;
228 } // namespace JS
230 namespace js {
232 using JS::detail::wrapper_comparison::operator==;
233 using JS::detail::wrapper_comparison::operator!=;
235 } // namespace js
237 #endif // js_ComparisonOperators_h