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 #pragma once
     18 
     19 #ifndef GSL_STRING_SPAN_H
     20 #define GSL_STRING_SPAN_H
     21 
     22 #include "gsl_assert"
     23 #include "gsl_util"
     24 #include "span"
     25 #include <cstdint>
     26 #include <cstring>
     27 #include <string>
     28 
     29 #ifdef _MSC_VER
     30 
     31 // No MSVC does constexpr fully yet
     32 #pragma push_macro("constexpr")
     33 #define constexpr /*constexpr*/
     34 
     35 #pragma warning(push)
     36 
     37 // blanket turn off warnings from CppCoreCheck for now
     38 // so people aren't annoyed by them when running the tool.
     39 // more targeted suppressions will be added in a future update to the GSL
     40 #pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495)
     41 
     42 // VS 2013 workarounds
     43 #if _MSC_VER <= 1800
     44 
     45 #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
     46 #define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE
     47 #define GSL_MSVC_NO_CPP14_STD_EQUAL
     48 #define GSL_MSVC_NO_DEFAULT_MOVE_CTOR
     49 
     50 // noexcept is not understood
     51 #ifndef GSL_THROW_ON_CONTRACT_VIOLATION
     52 #pragma push_macro("noexcept")
     53 #define noexcept /*noexcept*/
     54 #endif
     55 
     56 #endif // _MSC_VER <= 1800
     57 #endif // _MSC_VER
     58 
     59 // In order to test the library, we need it to throw exceptions that we can catch
     60 #ifdef GSL_THROW_ON_CONTRACT_VIOLATION
     61 
     62 #ifdef _MSC_VER
     63 #pragma push_macro("noexcept")
     64 #endif
     65 
     66 #define noexcept /*noexcept*/
     67 
     68 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
     69 
     70 namespace gsl
     71 {
     72 //
     73 // czstring and wzstring
     74 //
     75 // These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays)
     76 // that allow static analysis to help find bugs.
     77 //
     78 // There are no additional features/semantics that we can find a way to add inside the
     79 // type system for these types that will not either incur significant runtime costs or
     80 // (sometimes needlessly) break existing programs when introduced.
     81 //
     82 
     83 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
     84 using basic_zstring = CharT*;
     85 
     86 template <std::ptrdiff_t Extent = dynamic_extent>
     87 using czstring = basic_zstring<const char, Extent>;
     88 
     89 template <std::ptrdiff_t Extent = dynamic_extent>
     90 using cwzstring = basic_zstring<const wchar_t, Extent>;
     91 
     92 template <std::ptrdiff_t Extent = dynamic_extent>
     93 using zstring = basic_zstring<char, Extent>;
     94 
     95 template <std::ptrdiff_t Extent = dynamic_extent>
     96 using wzstring = basic_zstring<wchar_t, Extent>;
     97 
     98 namespace details
     99 {
    100     inline std::ptrdiff_t string_length(const char *str, std::ptrdiff_t n)
    101     {
    102         if (str == nullptr || n <= 0)
    103             return 0;
    104 
    105         span<const char> str_span{str, n};
    106 
    107         std::ptrdiff_t len = 0;
    108         while (len < n && str_span[len])
    109             len++;
    110 
    111         return len;
    112     }
    113 
    114     inline std::ptrdiff_t wstring_length(const wchar_t *str, std::ptrdiff_t n)
    115     {
    116         if (str == nullptr || n <= 0)
    117             return 0;
    118 
    119         span<const wchar_t> str_span{str, n};
    120 
    121         std::ptrdiff_t len = 0;
    122         while (len < n && str_span[len])
    123             len++;
    124 
    125         return len;
    126     }
    127 }
    128 
    129 //
    130 // ensure_sentinel()
    131 //
    132 // Provides a way to obtain an span from a contiguous sequence
    133 // that ends with a (non-inclusive) sentinel value.
    134 //
    135 // Will fail-fast if sentinel cannot be found before max elements are examined.
    136 //
    137 template <typename T, const T Sentinel>
    138 span<T, dynamic_extent> ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX)
    139 {
    140     auto cur = seq;
    141     while ((cur - seq) < max && *cur != Sentinel) ++cur;
    142     Ensures(*cur == Sentinel);
    143     return {seq, cur - seq};
    144 }
    145 
    146 //
    147 // ensure_z - creates a span for a czstring or cwzstring.
    148 // Will fail fast if a null-terminator cannot be found before
    149 // the limit of size_type.
    150 //
    151 template <typename T>
    152 inline span<T, dynamic_extent> ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX)
    153 {
    154     return ensure_sentinel<T, 0>(sz, max);
    155 }
    156 
    157 // TODO (neilmac) there is probably a better template-magic way to get the const and non-const
    158 // overloads to share an implementation
    159 inline span<char, dynamic_extent> ensure_z(char* const& sz, std::ptrdiff_t max)
    160 {
    161     auto len = details::string_length(sz, max);
    162     Ensures(sz[len] == 0);
    163     return {sz, len};
    164 }
    165 
    166 inline span<const char, dynamic_extent> ensure_z(const char* const& sz, std::ptrdiff_t max)
    167 {
    168     auto len = details::string_length(sz, max);
    169     Ensures(sz[len] == 0);
    170     return {sz, len};
    171 }
    172 
    173 inline span<wchar_t, dynamic_extent> ensure_z(wchar_t* const& sz, std::ptrdiff_t max)
    174 {
    175     auto len = details::wstring_length(sz, max);
    176     Ensures(sz[len] == 0);
    177     return {sz, len};
    178 }
    179 
    180 inline span<const wchar_t, dynamic_extent> ensure_z(const wchar_t* const& sz, std::ptrdiff_t max)
    181 {
    182     auto len = details::wstring_length(sz, max);
    183     Ensures(sz[len] == 0);
    184     return {sz, len};
    185 }
    186 
    187 template <typename T, size_t N>
    188 span<T, dynamic_extent> ensure_z(T (&sz)[N])
    189 {
    190     return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N));
    191 }
    192 
    193 template <class Cont>
    194 span<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_extent>
    195 ensure_z(Cont& cont)
    196 {
    197     return ensure_z(cont.data(), static_cast<std::ptrdiff_t>(cont.length()));
    198 }
    199 
    200 template <typename CharT, std::ptrdiff_t>
    201 class basic_string_span;
    202 
    203 namespace details
    204 {
    205     template <typename T>
    206     struct is_basic_string_span_oracle : std::false_type
    207     {
    208     };
    209 
    210     template <typename CharT, std::ptrdiff_t Extent>
    211     struct is_basic_string_span_oracle<basic_string_span<CharT, Extent>> : std::true_type
    212     {
    213     };
    214 
    215     template <typename T>
    216     struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>>
    217     {
    218     };
    219 
    220     template <typename T>
    221     struct length_func
    222     {
    223     };
    224 
    225     template <>
    226     struct length_func<char>
    227     {
    228         std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept
    229         {
    230             return details::string_length(ptr, length);
    231         }
    232     };
    233 
    234     template <>
    235     struct length_func<wchar_t>
    236     {
    237         std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept
    238         {
    239             return details::wstring_length(ptr, length);
    240         }
    241     };
    242 
    243     template <>
    244     struct length_func<const char>
    245     {
    246         std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept
    247         {
    248             return details::string_length(ptr, length);
    249         }
    250     };
    251 
    252     template <>
    253     struct length_func<const wchar_t>
    254     {
    255         std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept
    256         {
    257             return details::wstring_length(ptr, length);
    258         }
    259     };
    260 }
    261 
    262 //
    263 // string_span and relatives
    264 //
    265 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
    266 class basic_string_span
    267 {
    268 public:
    269     using element_type = CharT;
    270     using pointer = std::add_pointer_t<element_type>;
    271     using reference = std::add_lvalue_reference_t<element_type>;
    272     using const_reference = std::add_lvalue_reference_t<std::add_const_t<element_type>>;
    273     using impl_type = span<element_type, Extent>;
    274 
    275     using index_type = typename impl_type::index_type;
    276     using iterator = typename impl_type::iterator;
    277     using const_iterator = typename impl_type::const_iterator;
    278     using reverse_iterator = typename impl_type::reverse_iterator;
    279     using const_reverse_iterator = typename impl_type::const_reverse_iterator;
    280 
    281     // default (empty)
    282     constexpr basic_string_span() noexcept = default;
    283 
    284     // copy
    285     constexpr basic_string_span(const basic_string_span& other) noexcept = default;
    286 
    287 // move
    288 #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
    289     constexpr basic_string_span(basic_string_span&& other) noexcept = default;
    290 #else
    291     constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {}
    292 #endif
    293 
    294     // assign
    295     constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default;
    296 
    297 // move assign
    298 #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
    299     constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default;
    300 #else
    301     constexpr basic_string_span& operator=(basic_string_span&& other) noexcept
    302     {
    303         span_ = std::move(other.span_);
    304         return *this;
    305     }
    306 #endif
    307 
    308     // from nullptr
    309     constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {}
    310 
    311     constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {}
    312     constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {}
    313 
    314     // From static arrays - if 0-terminated, remove 0 from the view
    315     // All other containers allow 0s within the length, so we do not remove them
    316     template <size_t N>
    317     constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr))
    318     {
    319     }
    320 
    321     template <size_t N, class ArrayElementType = std::remove_const_t<element_type>>
    322     constexpr basic_string_span(std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
    323     {
    324     }
    325 
    326     template <size_t N, class ArrayElementType = std::remove_const_t<element_type>>
    327     constexpr basic_string_span(const std::array<ArrayElementType, N>& arr) noexcept : span_(arr)
    328     {
    329     }
    330 
    331     // Container signature should work for basic_string after C++17 version exists
    332     template <class Traits, class Allocator>
    333     constexpr basic_string_span(std::basic_string<element_type, Traits, Allocator>& str)
    334         : span_(&str[0], str.length())
    335     {
    336     }
    337 
    338     template <class Traits, class Allocator>
    339     constexpr basic_string_span(const std::basic_string<element_type, Traits, Allocator>& str)
    340         : span_(&str[0], str.length())
    341     {
    342     }
    343 
    344     // from containers. Containers must have a pointer type and data() function signatures
    345     template <class Container,
    346               class = std::enable_if_t<
    347                   !details::is_basic_string_span<Container>::value &&
    348                   std::is_convertible<typename Container::pointer, pointer>::value &&
    349                   std::is_convertible<typename Container::pointer,
    350                                       decltype(std::declval<Container>().data())>::value>>
    351     constexpr basic_string_span(Container& cont) : span_(cont)
    352     {
    353     }
    354 
    355     template <class Container,
    356               class = std::enable_if_t<
    357                   !details::is_basic_string_span<Container>::value &&
    358                   std::is_convertible<typename Container::pointer, pointer>::value &&
    359                   std::is_convertible<typename Container::pointer,
    360                                       decltype(std::declval<Container>().data())>::value>>
    361     constexpr basic_string_span(const Container& cont) : span_(cont)
    362     {
    363     }
    364 
    365     // from string_span
    366     template <
    367         class OtherValueType, std::ptrdiff_t OtherExtent,
    368         class = std::enable_if_t<std::is_convertible<
    369             typename basic_string_span<OtherValueType, OtherExtent>::impl_type, impl_type>::value>>
    370     constexpr basic_string_span(basic_string_span<OtherValueType, OtherExtent> other)
    371         : span_(other.data(), other.length())
    372     {
    373     }
    374 
    375     template <index_type Count>
    376     constexpr basic_string_span<element_type, Count> first() const
    377     {
    378         return {span_.template first<Count>()};
    379     }
    380 
    381     constexpr basic_string_span<element_type, dynamic_extent> first(index_type count) const
    382     {
    383         return {span_.first(count)};
    384     }
    385 
    386     template <index_type Count>
    387     constexpr basic_string_span<element_type, Count> last() const
    388     {
    389         return {span_.template last<Count>()};
    390     }
    391 
    392     constexpr basic_string_span<element_type, dynamic_extent> last(index_type count) const
    393     {
    394         return {span_.last(count)};
    395     }
    396 
    397     template <index_type Offset, index_type Count>
    398     constexpr basic_string_span<element_type, Count> subspan() const
    399     {
    400         return {span_.template subspan<Offset, Count>()};
    401     }
    402 
    403     constexpr basic_string_span<element_type, dynamic_extent>
    404     subspan(index_type offset, index_type count = dynamic_extent) const
    405     {
    406         return {span_.subspan(offset, count)};
    407     }
    408 
    409     constexpr reference operator[](index_type idx) const { return span_[idx]; }
    410     constexpr reference operator()(index_type idx) const { return span_[idx]; }
    411 
    412     constexpr pointer data() const { return span_.data(); }
    413 
    414     constexpr index_type length() const noexcept { return span_.size(); }
    415     constexpr index_type size() const noexcept { return span_.size(); }
    416     constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); }
    417     constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); }
    418     constexpr bool empty() const noexcept { return size() == 0; }
    419 
    420     constexpr iterator begin() const noexcept { return span_.begin(); }
    421     constexpr iterator end() const noexcept { return span_.end(); }
    422 
    423     constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); }
    424     constexpr const_iterator cend() const noexcept { return span_.cend(); }
    425 
    426     constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); }
    427     constexpr reverse_iterator rend() const noexcept { return span_.rend(); }
    428 
    429     constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); }
    430     constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); }
    431 
    432 private:
    433     static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
    434     {
    435         return {sz, details::length_func<element_type>()(sz, max)};
    436     }
    437 
    438     template <size_t N>
    439     static impl_type remove_z(element_type (&sz)[N])
    440     {
    441         return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
    442     }
    443 
    444     impl_type span_;
    445 };
    446 
    447 template <std::ptrdiff_t Extent = dynamic_extent>
    448 using string_span = basic_string_span<char, Extent>;
    449 
    450 template <std::ptrdiff_t Extent = dynamic_extent>
    451 using cstring_span = basic_string_span<const char, Extent>;
    452 
    453 template <std::ptrdiff_t Extent = dynamic_extent>
    454 using wstring_span = basic_string_span<wchar_t, Extent>;
    455 
    456 template <std::ptrdiff_t Extent = dynamic_extent>
    457 using cwstring_span = basic_string_span<const wchar_t, Extent>;
    458 
    459 //
    460 // to_string() allow (explicit) conversions from string_span to string
    461 //
    462 #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
    463 
    464 template <typename CharT, std::ptrdiff_t Extent>
    465 std::basic_string<typename std::remove_const<CharT>::type>
    466 to_string(basic_string_span<CharT, Extent> view)
    467 {
    468     return {view.data(), static_cast<size_t>(view.length())};
    469 }
    470 
    471 #else
    472 
    473 inline std::string to_string(cstring_span<> view)
    474 {
    475     return {view.data(), static_cast<size_t>(view.length())};
    476 }
    477 
    478 inline std::string to_string(string_span<> view)
    479 {
    480     return {view.data(), static_cast<size_t>(view.length())};
    481 }
    482 
    483 inline std::wstring to_string(cwstring_span<> view)
    484 {
    485     return {view.data(), static_cast<size_t>(view.length())};
    486 }
    487 
    488 inline std::wstring to_string(wstring_span<> view)
    489 {
    490     return {view.data(), static_cast<size_t>(view.length())};
    491 }
    492 
    493 #endif
    494 
    495 template <typename CharT,
    496 	  typename Traits = typename std::char_traits<CharT>,
    497 	  typename Allocator = std::allocator<CharT>,
    498 	  typename gCharT,
    499 	  std::ptrdiff_t Extent>
    500 std::basic_string<CharT, Traits, Allocator> to_basic_string(basic_string_span<gCharT, Extent> view)
    501 {
    502   return {view.data(), static_cast<size_t>(view.length())};
    503 }
    504   
    505 // zero-terminated string span, used to convert
    506 // zero-terminated spans to legacy strings
    507 template <typename CharT, std::ptrdiff_t Extent = dynamic_extent>
    508 class basic_zstring_span
    509 {
    510 public:
    511     using value_type = CharT;
    512     using const_value_type = std::add_const_t<CharT>;
    513 
    514     using pointer = std::add_pointer_t<value_type>;
    515     using const_pointer = std::add_pointer_t<const_value_type>;
    516 
    517     using zstring_type = basic_zstring<value_type, Extent>;
    518     using const_zstring_type = basic_zstring<const_value_type, Extent>;
    519 
    520     using impl_type = span<value_type, Extent>;
    521     using string_span_type = basic_string_span<value_type, Extent>;
    522 
    523     constexpr basic_zstring_span(impl_type s) noexcept : span_(s)
    524     {
    525         // expects a zero-terminated span
    526         Expects(s[s.size() - 1] == '\0');
    527     }
    528 
    529     // copy
    530     constexpr basic_zstring_span(const basic_zstring_span& other) = default;
    531 
    532 // move
    533 #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
    534     constexpr basic_zstring_span(basic_zstring_span&& other) = default;
    535 #else
    536     constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {}
    537 #endif
    538 
    539     // assign
    540     constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default;
    541 
    542 // move assign
    543 #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
    544     constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default;
    545 #else
    546     constexpr basic_zstring_span& operator=(basic_zstring_span&& other)
    547     {
    548         span_ = std::move(other.span_);
    549         return *this;
    550     }
    551 #endif
    552 
    553     constexpr bool empty() const noexcept { return span_.size() == 0; }
    554 
    555     constexpr string_span_type as_string_span() const noexcept
    556     {
    557         auto sz = span_.size();
    558         return span_.first(sz <= 0 ? 0 : sz - 1);
    559     }
    560 
    561     constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); }
    562 
    563     constexpr const_zstring_type assume_z() const noexcept { return span_.data(); }
    564 
    565 private:
    566     impl_type span_;
    567 };
    568 
    569 template <std::ptrdiff_t Max = dynamic_extent>
    570 using zstring_span = basic_zstring_span<char, Max>;
    571 
    572 template <std::ptrdiff_t Max = dynamic_extent>
    573 using wzstring_span = basic_zstring_span<wchar_t, Max>;
    574 
    575 template <std::ptrdiff_t Max = dynamic_extent>
    576 using czstring_span = basic_zstring_span<const char, Max>;
    577 
    578 template <std::ptrdiff_t Max = dynamic_extent>
    579 using cwzstring_span = basic_zstring_span<const wchar_t, Max>;
    580 
    581 // operator ==
    582 template <class CharT, std::ptrdiff_t Extent, class T,
    583           class = std::enable_if_t<
    584               details::is_basic_string_span<T>::value ||
    585               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
    586 bool operator==(const gsl::basic_string_span<CharT, Extent>& one, const T& other) noexcept
    587 {
    588     gsl::basic_string_span<std::add_const_t<CharT>> tmp(other);
    589 #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL
    590     return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin());
    591 #else
    592     return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
    593 #endif
    594 }
    595 
    596 template <class CharT, std::ptrdiff_t Extent, class T,
    597           class = std::enable_if_t<
    598               !details::is_basic_string_span<T>::value &&
    599               std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>>>::value>>
    600 bool operator==(const T& one, const gsl::basic_string_span<CharT, Extent>& other) noexcept
    601 {
    602     gsl::basic_string_span<std::add_const_t<CharT>> tmp(one);
    603 #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL
    604     return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin());
    605 #else
    606     return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
    607 #endif
    608 }
    609 
    610 // operator !=
    611 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    612           typename = std::enable_if_t<std::is_convertible<
    613               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    614 bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    615 {
    616     return !(one == other);
    617 }
    618 
    619 template <
    620     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    621     typename Dummy = std::enable_if_t<
    622         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    623         !gsl::details::is_basic_string_span<T>::value>>
    624 bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    625 {
    626     return !(one == other);
    627 }
    628 
    629 // operator<
    630 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    631           typename = std::enable_if_t<std::is_convertible<
    632               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    633 bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    634 {
    635     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
    636     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
    637 }
    638 
    639 template <
    640     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    641     typename Dummy = std::enable_if_t<
    642         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    643         !gsl::details::is_basic_string_span<T>::value>>
    644 bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    645 {
    646     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
    647     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
    648 }
    649 
    650 #ifndef _MSC_VER
    651 
    652 // VS treats temp and const containers as convertible to basic_string_span,
    653 // so the cases below are already covered by the previous operators
    654 
    655 template <
    656     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    657     typename DataType = typename T::value_type,
    658     typename Dummy = std::enable_if_t<
    659         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    660         std::is_convertible<DataType*, CharT*>::value &&
    661         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    662                      DataType>::value>>
    663 bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    664 {
    665     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
    666     return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
    667 }
    668 
    669 template <
    670     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    671     typename DataType = typename T::value_type,
    672     typename Dummy = std::enable_if_t<
    673         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    674         std::is_convertible<DataType*, CharT*>::value &&
    675         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    676                      DataType>::value>>
    677 bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    678 {
    679     gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
    680     return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
    681 }
    682 #endif
    683 
    684 // operator <=
    685 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    686           typename = std::enable_if_t<std::is_convertible<
    687               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    688 bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    689 {
    690     return !(other < one);
    691 }
    692 
    693 template <
    694     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    695     typename Dummy = std::enable_if_t<
    696         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    697         !gsl::details::is_basic_string_span<T>::value>>
    698 bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    699 {
    700     return !(other < one);
    701 }
    702 
    703 #ifndef _MSC_VER
    704 
    705 // VS treats temp and const containers as convertible to basic_string_span,
    706 // so the cases below are already covered by the previous operators
    707 
    708 template <
    709     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    710     typename DataType = typename T::value_type,
    711     typename Dummy = std::enable_if_t<
    712         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    713         std::is_convertible<DataType*, CharT*>::value &&
    714         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    715                      DataType>::value>>
    716 bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    717 {
    718     return !(other < one);
    719 }
    720 
    721 template <
    722     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    723     typename DataType = typename T::value_type,
    724     typename Dummy = std::enable_if_t<
    725         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    726         std::is_convertible<DataType*, CharT*>::value &&
    727         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    728                      DataType>::value>>
    729 bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    730 {
    731     return !(other < one);
    732 }
    733 #endif
    734 
    735 // operator>
    736 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    737           typename = std::enable_if_t<std::is_convertible<
    738               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    739 bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    740 {
    741     return other < one;
    742 }
    743 
    744 template <
    745     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    746     typename Dummy = std::enable_if_t<
    747         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    748         !gsl::details::is_basic_string_span<T>::value>>
    749 bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    750 {
    751     return other < one;
    752 }
    753 
    754 #ifndef _MSC_VER
    755 
    756 // VS treats temp and const containers as convertible to basic_string_span,
    757 // so the cases below are already covered by the previous operators
    758 
    759 template <
    760     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    761     typename DataType = typename T::value_type,
    762     typename Dummy = std::enable_if_t<
    763         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    764         std::is_convertible<DataType*, CharT*>::value &&
    765         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    766                      DataType>::value>>
    767 bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    768 {
    769     return other < one;
    770 }
    771 
    772 template <
    773     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    774     typename DataType = typename T::value_type,
    775     typename Dummy = std::enable_if_t<
    776         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    777         std::is_convertible<DataType*, CharT*>::value &&
    778         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    779                      DataType>::value>>
    780 bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    781 {
    782     return other < one;
    783 }
    784 #endif
    785 
    786 // operator >=
    787 template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    788           typename = std::enable_if_t<std::is_convertible<
    789               T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>>
    790 bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    791 {
    792     return !(one < other);
    793 }
    794 
    795 template <
    796     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    797     typename Dummy = std::enable_if_t<
    798         std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value &&
    799         !gsl::details::is_basic_string_span<T>::value>>
    800 bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    801 {
    802     return !(one < other);
    803 }
    804 
    805 #ifndef _MSC_VER
    806 
    807 // VS treats temp and const containers as convertible to basic_string_span,
    808 // so the cases below are already covered by the previous operators
    809 
    810 template <
    811     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    812     typename DataType = typename T::value_type,
    813     typename Dummy = std::enable_if_t<
    814         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    815         std::is_convertible<DataType*, CharT*>::value &&
    816         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    817                      DataType>::value>>
    818 bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
    819 {
    820     return !(one < other);
    821 }
    822 
    823 template <
    824     typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T,
    825     typename DataType = typename T::value_type,
    826     typename Dummy = std::enable_if_t<
    827         !gsl::details::is_span<T>::value && !gsl::details::is_basic_string_span<T>::value &&
    828         std::is_convertible<DataType*, CharT*>::value &&
    829         std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>,
    830                      DataType>::value>>
    831 bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
    832 {
    833     return !(one < other);
    834 }
    835 #endif
    836 } // namespace GSL
    837 
    838 #ifdef _MSC_VER
    839 
    840 #pragma warning(pop)
    841 
    842 #undef constexpr
    843 #pragma pop_macro("constexpr")
    844 
    845 // VS 2013 workarounds
    846 #if _MSC_VER <= 1800
    847 
    848 #ifndef GSL_THROW_ON_CONTRACT_VIOLATION
    849 #undef noexcept
    850 #pragma pop_macro("noexcept")
    851 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
    852 
    853 #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
    854 #undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE
    855 #undef GSL_MSVC_NO_CPP14_STD_EQUAL
    856 #undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR
    857 
    858 #endif // _MSC_VER <= 1800
    859 #endif // _MSC_VER
    860 
    861 #if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
    862 
    863 #undef noexcept
    864 
    865 #ifdef _MSC_VER
    866 #pragma pop_macro("noexcept")
    867 #endif
    868 
    869 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
    870 #endif // GSL_STRING_SPAN_H
    871