Home | History | Annotate | Download | only in gsl
      1 ///////////////////////////////////////////////////////////////////////////////
      2 //
      3 // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
      4 //
      5 // This code is licensed under the MIT License (MIT).
      6 //
      7 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      8 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      9 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     10 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     11 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     12 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     13 // THE SOFTWARE.
     14 //
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 #ifndef GSL_STRING_SPAN_H
     18 #define GSL_STRING_SPAN_H
     19 
     20 #include <gsl/gsl_assert> // for Ensures, Expects
     21 #include <gsl/gsl_util>   // for narrow_cast
     22 #include <gsl/span>       // for operator!=, operator==, dynamic_extent
     23 #include <gsl/pointers>   // for not_null
     24 
     25 #include <algorithm> // for equal, lexicographical_compare
     26 #include <array>     // for array
     27 #include <cstddef>   // for ptrdiff_t, size_t, nullptr_t
     28 #include <cstdint>   // for PTRDIFF_MAX
     29 #include <cstring>
     30 #include <string>      // for basic_string, allocator, char_traits
     31 #include <type_traits> // for declval, is_convertible, enable_if_t, add_...
     32 
     33 #if defined(_MSC_VER) && !defined(__clang__)
     34 #pragma warning(push)
     35 
     36 // Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool.
     37 #pragma warning(disable : 26446) // TODO: bug in parser - attributes and templates
     38 #pragma warning(disable : 26481) // TODO: suppress does not work inside templates sometimes
     39 
     40 #if _MSC_VER < 1910
     41 #pragma push_macro("constexpr")
     42 #define constexpr /*constexpr*/
     43 
     44 #endif // _MSC_VER < 1910
     45 #endif // _MSC_VER
     46 
     47 namespace gsl
     48 {
     49 //
     50 // czstring and wzstring
     51 //
     52 // These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays)
     53 // that allow static analysis to help find bugs.
     54 //
     55 // There are no additional features/semantics that we can find a way to add inside the
     56 // type system for these types that will not either incur significant runtime costs or
     57 // (sometimes needlessly) break existing programs when introduced.
     58 //
     59 
     60 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
     61 using basic_zstring = CharT*;
     62 
     63 template <std::ptrdiff_t Extent = dynamic_extent>
     64 using czstring = basic_zstring<const char, Extent>;
     65 
     66 template <std::ptrdiff_t Extent = dynamic_extent>
     67 using cwzstring = basic_zstring<const wchar_t, Extent>;
     68 
     69 template <std::ptrdiff_t Extent = dynamic_extent>
     70 using cu16zstring = basic_zstring<const char16_t, Extent>;
     71 
     72 template <std::ptrdiff_t Extent = dynamic_extent>
     73 using cu32zstring = basic_zstring<const char32_t, Extent>;
     74 
     75 template <std::ptrdiff_t Extent = dynamic_extent>
     76 using zstring = basic_zstring<char, Extent>;
     77 
     78 template <std::ptrdiff_t Extent = dynamic_extent>
     79 using wzstring = basic_zstring<wchar_t, Extent>;
     80 
     81 template <std::ptrdiff_t Extent = dynamic_extent>
     82 using u16zstring = basic_zstring<char16_t, Extent>;
     83 
     84 template <std::ptrdiff_t Extent = dynamic_extent>
     85 using u32zstring = basic_zstring<char32_t, Extent>;
     86 
     87 namespace details
     88 {
     89     template <class CharT>
     90     std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n)
     91     {
     92         if (str == nullptr || n <= 0) return 0;
     93 
     94         const span<const CharT> str_span{str, n};
     95 
     96         std::ptrdiff_t len = 0;
     97         while (len < n && str_span[len]) len++;
     98 
     99         return len;
    100     }
    101 } // namespace details
    102 
    103 //
    104 // ensure_sentinel()
    105 //
    106 // Provides a way to obtain an span from a contiguous sequence
    107 // that ends with a (non-inclusive) sentinel value.
    108 //
    109 // Will fail-fast if sentinel cannot be found before max elements are examined.
    110 //
    111 template <typename T, const T Sentinel>
    112 span<T, dynamic_extent> ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX)
    113 {
    114     Ensures(seq != nullptr);
    115 
    116     GSL_SUPPRESS(f.23) // NO-FORMAT: attribute // TODO: false positive // TODO: suppress does not work
    117     auto cur = seq;
    118     Ensures(cur != nullptr); // workaround for removing the warning
    119 
    120     GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute // TODO: suppress does not work
    121     while ((cur - seq) < max && *cur != Sentinel) ++cur;
    122     Ensures(*cur == Sentinel);
    123     return {seq, cur - seq};
    124 }
    125 
    126 //
    127 // ensure_z - creates a span for a zero terminated strings.
    128 // Will fail fast if a null-terminator cannot be found before
    129 // the limit of size_type.
    130 //
    131 template <typename CharT>
    132 span<CharT, dynamic_extent> ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX)
    133 {
    134     return ensure_sentinel<CharT, CharT(0)>(sz, max);
    135 }
    136 
    137 template <typename CharT, std::size_t N>
    138 span<CharT, dynamic_extent> ensure_z(CharT (&sz)[N])
    139 {
    140     return ensure_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
    141 }
    142 
    143 template <class Cont>
    144 span<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_extent>
    145 ensure_z(Cont& cont)
    146 {
    147     return ensure_z(cont.data(), narrow_cast<std::ptrdiff_t>(cont.size()));
    148 }
    149 
    150 template <typename CharT, std::ptrdiff_t>
    151 class basic_string_span;
    152 
    153 namespace details {
    154     template <typename T>
    155     struct is_basic_string_span_oracle : std::false_type
    156     {
    157     };
    158 
    159     template <typename CharT, std::ptrdiff_t Extent>
    160     struct is_basic_string_span_oracle<basic_string_span<CharT, Extent>> : std::true_type
    161     {
    162     };
    163 
    164     template <typename T>
    165     struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>>
    166     {
    167     };
    168 } // namespace details
    169 
    170 //
    171 // string_span and relatives
    172 //
    173 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
    174 class basic_string_span
    175 {
    176 public:
    177     using element_type = CharT;
    178     using pointer = std::add_pointer_t<element_type>;
    179     using reference = std::add_lvalue_reference_t<element_type>;
    180     using const_reference = std::add_lvalue_reference_t<std::add_const_t<element_type>>;
    181     using impl_type = span<element_type, Extent>;
    182 
    183     using index_type = typename impl_type::index_type;
    184     using iterator = typename impl_type::iterator;
    185     using const_iterator = typename impl_type::const_iterator;
    186     using reverse_iterator = typename impl_type::reverse_iterator;
    187     using const_reverse_iterator = typename impl_type::const_reverse_iterator;
    188 
    189     // default (empty)
    190     constexpr basic_string_span() noexcept = default;
    191 
    192     // copy
    193     constexpr basic_string_span(const basic_string_span& other) noexcept = default;
    194 
    195     // assign
    196     constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default;
    197 
    198     constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {}
    199     constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {}
    200 
    201     // From static arrays - if 0-terminated, remove 0 from the view
    202     // All other containers allow 0s within the length, so we do not remove them
    203     template <std::size_t N>
    204     constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr))
    205     {}
    206 
    207     template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
    208     constexpr basic_string_span(std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
    209     {}
    210 
    211     template <std::size_t N, class ArrayElementType = std::remove_const_t<element_type>>
    212     constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
    213     {}
    214 
    215     // Container signature should work for basic_string after C++17 version exists
    216     template <class Traits, class Allocator>
    217     // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug
    218     constexpr basic_string_span(std::basic_string<element_type, Traits, Allocator>& str)
    219         : span_(&str[0], narrow_cast<std::ptrdiff_t>(str.length()))
    220     {}
    221 
    222     template <class Traits, class Allocator>
    223     constexpr basic_string_span(const std::basic_string<element_type, Traits, Allocator>& str)
    224         : span_(&str[0], str.length())
    225     {}
    226 
    227     // from containers. Containers must have a pointer type and data() function signatures
    228     template <class Container,
    229               class = std::enable_if_t<
    230                   !details::is_basic_string_span<Container>::value &&
    231                   std::is_convertible<typename Container::pointer, pointer>::value &&
    232                   std::is_convertible<typename Container::pointer,
    233                                       decltype(std::declval<Container>().data())>::value>>
    234     constexpr basic_string_span(Container& cont) : span_(cont)
    235     {}
    236 
    237     template <class Container,
    238               class = std::enable_if_t<
    239                   !details::is_basic_string_span<Container>::value &&
    240                   std::is_convertible<typename Container::pointer, pointer>::value &&
    241                   std::is_convertible<typename Container::pointer,
    242                                       decltype(std::declval<Container>().data())>::value>>
    243     constexpr basic_string_span(const Container& cont) : span_(cont)
    244     {}
    245 
    246     // from string_span
    247     template <
    248         class OtherValueType, std::ptrdiff_t OtherExtent,
    249         class = std::enable_if_t<std::is_convertible<
    250             typename basic_string_span<OtherValueType, OtherExtent>::impl_type, impl_type>::value>>
    251     constexpr basic_string_span(basic_string_span<OtherValueType, OtherExtent> other)
    252         : span_(other.data(), other.length())
    253     {}
    254 
    255     template <index_type Count>
    256     constexpr basic_string_span<element_type, Count> first() const
    257     {
    258         return {span_.template first<Count>()};
    259     }
    260 
    261     constexpr basic_string_span<element_type, dynamic_extent> first(index_type count) const
    262     {
    263         return {span_.first(count)};
    264     }
    265 
    266     template <index_type Count>
    267     constexpr basic_string_span<element_type, Count> last() const
    268     {
    269         return {span_.template last<Count>()};
    270     }
    271 
    272     constexpr basic_string_span<element_type, dynamic_extent> last(index_type count) const
    273     {
    274         return {span_.last(count)};
    275     }
    276 
    277     template <index_type Offset, index_type Count>
    278     constexpr basic_string_span<element_type, Count> subspan() const
    279     {
    280         return {span_.template subspan<Offset, Count>()};
    281     }
    282 
    283     constexpr basic_string_span<element_type, dynamic_extent>
    284     subspan(index_type offset, index_type count = dynamic_extent) const
    285     {
    286         return {span_.subspan(offset, count)};
    287     }
    288 
    289     constexpr reference operator[](index_type idx) const { return span_[idx]; }
    290     constexpr reference operator()(index_type idx) const { return span_[idx]; }
    291 
    292     constexpr pointer data() const { return span_.data(); }
    293 
    294     constexpr index_type length() const noexcept { return span_.size(); }
    295     constexpr index_type size() const noexcept { return span_.size(); }
    296     constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); }
    297     constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); }
    298     constexpr bool empty() const noexcept { return size() == 0; }
    299 
    300     constexpr iterator begin() const noexcept { return span_.begin(); }
    301     constexpr iterator end() const noexcept { return span_.end(); }
    302 
    303     constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); }
    304     constexpr const_iterator cend() const noexcept { return span_.cend(); }
    305 
    306     constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); }
    307     constexpr reverse_iterator rend() const noexcept { return span_.rend(); }
    308 
    309     constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); }
    310     constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); }
    311 
    312 private:
    313     static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
    314     {
    315         return {sz, details::string_length(sz, max)};
    316     }
    317 
    318     template <std::size_t N>
    319     static impl_type remove_z(element_type (&sz)[N])
    320     {
    321         return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
    322     }
    323 
    324     impl_type span_;
    325 };
    326 
    327 template <std::ptrdiff_t Extent = dynamic_extent>
    328 using string_span = basic_string_span<char, Extent>;
    329 
    330 template <std::ptrdiff_t Extent = dynamic_extent>
    331 using cstring_span = basic_string_span<const char, Extent>;
    332 
    333 template <std::ptrdiff_t Extent = dynamic_extent>
    334 using wstring_span = basic_string_span<wchar_t, Extent>;
    335 
    336 template <std::ptrdiff_t Extent = dynamic_extent>
    337 using cwstring_span = basic_string_span<const wchar_t, Extent>;
    338 
    339 template <std::ptrdiff_t Extent = dynamic_extent>
    340 using u16string_span = basic_string_span<char16_t, Extent>;
    341 
    342 template <std::ptrdiff_t Extent = dynamic_extent>
    343 using cu16string_span = basic_string_span<const char16_t, Extent>;
    344 
    345 template <std::ptrdiff_t Extent = dynamic_extent>
    346 using u32string_span = basic_string_span<char32_t, Extent>;
    347 
    348 template <std::ptrdiff_t Extent = dynamic_extent>
    349 using cu32string_span = basic_string_span<const char32_t, Extent>;
    350 
    351 //
    352 // to_string() allow (explicit) conversions from string_span to string
    353 //
    354 
    355 template <typename CharT, std::ptrdiff_t Extent>
    356 std::basic_string<typename std::remove_const<CharT>::type>
    357 to_string(basic_string_span<CharT, Extent> view)
    358 {
    359     return {view.data(), narrow_cast<std::size_t>(view.length())};
    360 }
    361 
    362 template <typename CharT, typename Traits = typename std::char_traits<CharT>,
    363           typename Allocator = std::allocator<CharT>, typename gCharT, std::ptrdiff_t Extent>
    364 std::basic_string<CharT, Traits, Allocator> to_basic_string(basic_string_span<gCharT, Extent> view)
    365 {
    366     return {view.data(), narrow_cast<std::size_t>(view.length())};
    367 }
    368 
    369 template <class ElementType, std::ptrdiff_t Extent>
    370 basic_string_span<const byte, details::calculate_byte_size<ElementType, Extent>::value>
    371 as_bytes(basic_string_span<ElementType, Extent> s) noexcept
    372 {
    373     GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
    374     return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
    375 }
    376 
    377 template <class ElementType, std::ptrdiff_t Extent,
    378           class = std::enable_if_t<!std::is_const<ElementType>::value>>
    379 basic_string_span<byte, details::calculate_byte_size<ElementType, Extent>::value>
    380 as_writeable_bytes(basic_string_span<ElementType, Extent> s) noexcept
    381 {
    382     GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
    383     return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
    384 }
    385 
    386 // zero-terminated string span, used to convert
    387 // zero-terminated spans to legacy strings
    388 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
    389 class basic_zstring_span {
    390 public:
    391     using value_type = CharT;
    392     using const_value_type = std::add_const_t<CharT>;
    393 
    394     using pointer = std::add_pointer_t<value_type>;
    395     using const_pointer = std::add_pointer_t<const_value_type>;
    396 
    397     using zstring_type = basic_zstring<value_type, Extent>;
    398     using const_zstring_type = basic_zstring<const_value_type, Extent>;
    399 
    400     using impl_type = span<value_type, Extent>;
    401     using string_span_type = basic_string_span<value_type, Extent>;
    402 
    403     constexpr basic_zstring_span(impl_type s) : span_(s)
    404     {
    405         // expects a zero-terminated span
    406         Expects(s[s.size() - 1] == '\0');
    407     }
    408 
    409     // copy
    410     constexpr basic_zstring_span(const basic_zstring_span& other) = default;
    411 
    412     // move
    413     constexpr basic_zstring_span(basic_zstring_span&& other) = default;
    414 
    415     // assign
    416     constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default;
    417 
    418     // move assign
    419     constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default;
    420 
    421     constexpr bool empty() const noexcept { return span_.size() == 0; }
    422 
    423     constexpr string_span_type as_string_span() const noexcept
    424     {
    425         const auto sz = span_.size();
    426         return {span_.data(), sz > 1 ? sz - 1 : 0};
    427     }
    428     constexpr string_span_type ensure_z() const { return gsl::ensure_z(span_); }
    429 
    430     constexpr const_zstring_type assume_z() const noexcept { return span_.data(); }
    431 
    432 private:
    433     impl_type span_;
    434 };
    435 
    436 template <std::ptrdiff_t Max = dynamic_extent>
    437 using zstring_span = basic_zstring_span<char, Max>;
    438 
    439 template <std::ptrdiff_t Max = dynamic_extent>
    440 using wzstring_span = basic_zstring_span<wchar_t, Max>;
    441 
    442 template <std::ptrdiff_t Max = dynamic_extent>
    443 using u16zstring_span = basic_zstring_span<char16_t, Max>;
    444 
    445 template <std::ptrdiff_t Max = dynamic_extent>
    446 using u32zstring_span = basic_zstring_span<char32_t, Max>;
    447 
    448 template <std::ptrdiff_t Max = dynamic_extent>
    449 using czstring_span = basic_zstring_span<const char, Max>;
    450 
    451 template <std::ptrdiff_t Max = dynamic_extent>
    452 using cwzstring_span = basic_zstring_span<const wchar_t, Max>;
    453 
    454 template <std::ptrdiff_t Max = dynamic_extent>
    455 using cu16zstring_span = basic_zstring_span<const char16_t, Max>;
    456 
    457 template <std::ptrdiff_t Max = dynamic_extent>
    458 using cu32zstring_span = basic_zstring_span<const char32_t, Max>;
    459 
    460 // operator ==
    461 template <class CharT, std::ptrdiff_t Extent, class T,
    462           class = std::enable_if_t<
    463               details::is_basic_string_span<T>::value ||
    464               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
    465 bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other)
    466 {
    467     const gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
    468     return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
    469 }
    470 
    471 template <class CharT, std::ptrdiff_t Extent, class T,
    472           class = std::enable_if_t<
    473               !details::is_basic_string_span<T>::value &&
    474               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
    475 bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other)
    476 {
    477     const gsl::basic_string_span<std::add_const_t<CharT>> tmp(one);
    478     return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
    479 }
    480 
    481 // operator !=
    482 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    483           typename = std::enable_if_t<std::is_convertible<
    484               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    485 bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other)
    486 {
    487     return !(one == other);
    488 }
    489 
    490 template <
    491     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    492     typename = std::enable_if_t<
    493         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    494         !gsl::details::is_basic_string_span<T>::value>>
    495 bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other)
    496 {
    497     return !(one == other);
    498 }
    499 
    500 // operator<
    501 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    502           typename = std::enable_if_t<std::is_convertible<
    503               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    504 bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other)
    505 {
    506     const gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
    507     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
    508 }
    509 
    510 template <
    511     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    512     typename = std::enable_if_t<
    513         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    514         !gsl::details::is_basic_string_span<T>::value>>
    515 bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other)
    516 {
    517     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
    518     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
    519 }
    520 
    521 #ifndef _MSC_VER
    522 
    523 // VS treats temp and const containers as convertible to basic_string_span,
    524 // so the cases below are already covered by the previous operators
    525 
    526 template <
    527     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    528     typename DataType = typename T::value_type,
    529     typename = std::enable_if_t<
    530         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    531         std::is_convertible<DataType*, CharT*>::value &&
    532         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    533                      DataType>::value>>
    534 bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other)
    535 {
    536     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
    537     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
    538 }
    539 
    540 template <
    541     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    542     typename DataType = typename T::value_type,
    543     typename = std::enable_if_t<
    544         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    545         std::is_convertible<DataType*, CharT*>::value &&
    546         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    547                      DataType>::value>>
    548 bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other)
    549 {
    550     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
    551     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
    552 }
    553 #endif
    554 
    555 // operator <=
    556 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    557           typename = std::enable_if_t<std::is_convertible<
    558               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    559 bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other)
    560 {
    561     return !(other < one);
    562 }
    563 
    564 template <
    565     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    566     typename = std::enable_if_t<
    567         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    568         !gsl::details::is_basic_string_span<T>::value>>
    569 bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other)
    570 {
    571     return !(other < one);
    572 }
    573 
    574 #ifndef _MSC_VER
    575 
    576 // VS treats temp and const containers as convertible to basic_string_span,
    577 // so the cases below are already covered by the previous operators
    578 
    579 template <
    580     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    581     typename DataType = typename T::value_type,
    582     typename = std::enable_if_t<
    583         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    584         std::is_convertible<DataType*, CharT*>::value &&
    585         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    586                      DataType>::value>>
    587 bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other)
    588 {
    589     return !(other < one);
    590 }
    591 
    592 template <
    593     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    594     typename DataType = typename T::value_type,
    595     typename = std::enable_if_t<
    596         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    597         std::is_convertible<DataType*, CharT*>::value &&
    598         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    599                      DataType>::value>>
    600 bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other)
    601 {
    602     return !(other < one);
    603 }
    604 #endif
    605 
    606 // operator>
    607 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    608           typename = std::enable_if_t<std::is_convertible<
    609               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    610 bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other)
    611 {
    612     return other < one;
    613 }
    614 
    615 template <
    616     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    617     typename = std::enable_if_t<
    618         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    619         !gsl::details::is_basic_string_span<T>::value>>
    620 bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other)
    621 {
    622     return other < one;
    623 }
    624 
    625 #ifndef _MSC_VER
    626 
    627 // VS treats temp and const containers as convertible to basic_string_span,
    628 // so the cases below are already covered by the previous operators
    629 
    630 template <
    631     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    632     typename DataType = typename T::value_type,
    633     typename = std::enable_if_t<
    634         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    635         std::is_convertible<DataType*, CharT*>::value &&
    636         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    637                      DataType>::value>>
    638 bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other)
    639 {
    640     return other < one;
    641 }
    642 
    643 template <
    644     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    645     typename DataType = typename T::value_type,
    646     typename = std::enable_if_t<
    647         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    648         std::is_convertible<DataType*, CharT*>::value &&
    649         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    650                      DataType>::value>>
    651 bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other)
    652 {
    653     return other < one;
    654 }
    655 #endif
    656 
    657 // operator >=
    658 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    659           typename = std::enable_if_t<std::is_convertible<
    660               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    661 bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other)
    662 {
    663     return !(one < other);
    664 }
    665 
    666 template <
    667     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    668     typename = std::enable_if_t<
    669         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    670         !gsl::details::is_basic_string_span<T>::value>>
    671 bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other)
    672 {
    673     return !(one < other);
    674 }
    675 
    676 #ifndef _MSC_VER
    677 
    678 // VS treats temp and const containers as convertible to basic_string_span,
    679 // so the cases below are already covered by the previous operators
    680 
    681 template <
    682     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    683     typename DataType = typename T::value_type,
    684     typename = std::enable_if_t<
    685         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    686         std::is_convertible<DataType*, CharT*>::value &&
    687         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    688                      DataType>::value>>
    689 bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other)
    690 {
    691     return !(one < other);
    692 }
    693 
    694 template <
    695     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    696     typename DataType = typename T::value_type,
    697     typename = std::enable_if_t<
    698         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    699         std::is_convertible<DataType*, CharT*>::value &&
    700         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    701                      DataType>::value>>
    702 bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other)
    703 {
    704     return !(one < other);
    705 }
    706 #endif
    707 } // namespace gsl
    708 
    709 #if defined(_MSC_VER) && !defined(__clang__)
    710 #pragma warning(pop)
    711 
    712 #if _MSC_VER < 1910
    713 #undef constexpr
    714 #pragma pop_macro("constexpr")
    715 
    716 #endif // _MSC_VER < 1910
    717 #endif // _MSC_VER
    718 
    719 #endif // GSL_STRING_SPAN_H
    720