tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ComparisonOperators.h (9711B)


      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/. */
      6 
      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.
     12 */
     13 
     14 #ifndef js_ComparisonOperators_h
     15 #define js_ComparisonOperators_h
     16 
     17 #include <type_traits>  // std::false_type, std::true_type, std::enable_if_t, std::is_pointer_v, std::remove_pointer_t
     18 
     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|:
     21 //
     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.)
     32 //
     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.
     35 //
     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.
     39 //
     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.
     43 
     44 namespace JS {
     45 
     46 namespace detail {
     47 
     48 // By default, comparison ops are not supported for types.
     49 template <typename T>
     50 struct DefineComparisonOps : std::false_type {};
     51 
     52 // Define functions for the core equality operations, that the actual operators
     53 // can all invoke.
     54 
     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);
     61 }
     62 
     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;
     70 }
     71 
     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;
     79 }
     80 
     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.
     87 //
     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 {
     93 
     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);
    101 }
    102 
    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);
    109 }
    110 
    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);
    116 }
    117 
    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);
    122 }
    123 
    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);
    128 }
    129 
    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);
    134 }
    135 
    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);
    145 }
    146 
    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);
    154 }
    155 
    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);
    163 }
    164 
    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);
    172 }
    173 
    174 // For wrappers around a pointer type, comparisons between a wrapper object
    175 // and |nullptr|.
    176 //
    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.
    187 //
    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);
    194 }
    195 
    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);
    200 }
    201 
    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);
    206 }
    207 
    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);
    212 }
    213 
    214 }  // namespace wrapper_comparison
    215 
    216 }  // namespace detail
    217 
    218 }  // namespace JS
    219 
    220 // Expose wrapper-supporting |operator==| and |operator!=| in the namespaces of
    221 // all SpiderMonkey's wrapper classes that support comparisons.
    222 
    223 namespace JS {
    224 
    225 using JS::detail::wrapper_comparison::operator==;
    226 using JS::detail::wrapper_comparison::operator!=;
    227 
    228 }  // namespace JS
    229 
    230 namespace js {
    231 
    232 using JS::detail::wrapper_comparison::operator==;
    233 using JS::detail::wrapper_comparison::operator!=;
    234 
    235 }  // namespace js
    236 
    237 #endif  // js_ComparisonOperators_h