From 87c5daa6c4c00d7660e5343d8d1dd13e300390a5 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 4 Dec 2015 14:23:13 -0800 Subject: [PATCH 1/4] Fixed operators and constructors for string_span --- include/string_span.h | 161 +++++++++++++++++++++++++++++------- tests/string_span_tests.cpp | 145 +++++++++++++++++++++++++++++--- 2 files changed, 262 insertions(+), 44 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index cc87068..8418029 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// /////////////////////////////////////////////////////////////////////////////// #pragma once @@ -243,17 +243,17 @@ public: // move assign constexpr basic_string_span& operator=(basic_string_span&& other) = default; + // from nullptr + constexpr basic_string_span(std::nullptr_t ptr) noexcept + : span_(ptr) + {} + // from nullptr and length constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept : span_(ptr, length) {} - // For pointers and static arrays - if 0-terminated, remove 0 from the view - - // from raw data and length - constexpr basic_string_span(pointer ptr, size_type length) noexcept - : span_(remove_z(ptr, length)) - {} + // From static arrays - if 0-terminated, remove 0 from the view // from static arrays and string literals template @@ -263,6 +263,11 @@ public: // Those allow 0s within the length, so we do not remove them + // from raw data and length + constexpr basic_string_span(pointer ptr, size_type length) noexcept + : span_(ptr, length) + {} + // from string constexpr basic_string_span(std::string& s) noexcept : span_(&(s.at(0)), narrow_cast(s.length())) @@ -523,32 +528,126 @@ template using wzstring_builder = basic_zstring_builder; } -template -bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +// operator == +template , Extent>>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept { - return std::equal(one.begin(), one.end(), other.begin(), other.end()); + gsl::basic_string_span, Extent> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template -bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept { + gsl::basic_string_span, Extent> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +// operator != +template , Extent>>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} + +// operator< +template , Extent>>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); } -template -bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +// operator <= +template , Extent>>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept { return !(other < one); } -template -bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} + +// operator> +template , Extent>>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept { return other < one; } -template -bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} + +// operator >= +template , Extent>>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); } diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index a966955..18fda26 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -14,11 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// - #include +#include #include #include -#include using namespace std; using namespace gsl; @@ -126,6 +125,7 @@ SUITE(string_span_tests) const char* ptr = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z("Hello"); // comparison to literal CHECK(span == cstring_span<>("Hello")); @@ -148,6 +148,12 @@ SUITE(string_span_tests) // comparison to vector of charaters with no null termination CHECK(span == cstring_span<>(vec)); + // comparison to span + CHECK(span == cstring_span<>(sp)); + + // comparison to string_span + CHECK(span == span); + // comparison of the original data to string CHECK(span.data() == std::string("Hello")); } @@ -162,6 +168,7 @@ SUITE(string_span_tests) char* ptr = ar; std::string str = "Hello"; std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z(ar1); // comparison to static array with no null termination CHECK(span == string_span<>(ar)); @@ -180,52 +187,133 @@ SUITE(string_span_tests) // comparison to vector of charaters with no null termination CHECK(span == string_span<>(vec)); + + // comparison to span + CHECK(span == string_span<>(sp)); + + // comparison to string_span + CHECK(span == span); } -#ifdef CONFIRM_COMPILATION_ERRORS + { + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z("Hello"); + cstring_span<> span = "Hello"; + // const span, const other type + CHECK(span == "Hello"); CHECK(span == ar); CHECK(span == ar1); CHECK(span == ar2); +#ifdef CONFIRM_COMPILATION_ERRORS + const char* ptr = "Hello"; CHECK(span == ptr); +#endif CHECK(span == str); CHECK(span == vec); + CHECK(span == sp); + + CHECK("Hello" == span); + CHECK(ar == span); + CHECK(ar1 == span); + CHECK(ar2 == span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(ptr == span); +#endif + CHECK(str == span); + CHECK(vec == span); + CHECK(sp == span); + + // const span, non-const other type char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; char _ar1[] = "Hello"; char _ar2[10] = "Hello"; - char* _ptr = _ar1; + char* _ptr = _ar; std::string _str = "Hello"; std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span _sp{ _ar, 5 }; CHECK(span == _ar); CHECK(span == _ar1); CHECK(span == _ar2); +#ifdef CONFIRM_COMPILATION_ERRORS CHECK(span == _ptr); +#endif CHECK(span == _str); CHECK(span == _vec); + CHECK(span == _sp); - string_span<> _span{ _ptr }; + CHECK(_ar == span); + CHECK(_ar1 == span); + CHECK(_ar2 == span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_ptr == span); +#endif + CHECK(_str == span); + CHECK(_vec == span); + CHECK(_sp == span); + string_span<> _span{ _ptr, 5 }; + + // non-const span, non-const other type + CHECK(_span == _ar); CHECK(_span == _ar1); CHECK(_span == _ar2); +#ifdef CONFIRM_COMPILATION_ERRORS CHECK(_span == _ptr); +#endif CHECK(_span == _str); CHECK(_span == _vec); + CHECK(_span == _sp); + + CHECK(_ar == _span); + CHECK(_ar1 == _span); + CHECK(_ar2 == _span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(_ptr == _span); +#endif + CHECK(_str == _span); + CHECK(_vec == _span); + CHECK(_sp == _span); + + // non-const span, const other type CHECK(_span == "Hello"); CHECK(_span == ar); CHECK(_span == ar1); CHECK(_span == ar2); +#ifdef CONFIRM_COMPILATION_ERRORS CHECK(_span == ptr); +#endif CHECK(_span == str); CHECK(_span == vec); - } + CHECK(_span == sp); + + CHECK("Hello" == _span); + CHECK(ar == _span); + CHECK(ar1 == _span); + CHECK(ar2 == _span); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(ptr == _span); #endif + CHECK(str == _span); + CHECK(vec == _span); + CHECK(sp == _span); + + // two spans + + CHECK(_span == span); + CHECK(span == _span); + } { std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; @@ -238,7 +326,6 @@ SUITE(string_span_tests) } } - TEST(ComparisonAndImplicitConstructors) { { @@ -310,7 +397,7 @@ SUITE(string_span_tests) CHECK(span >= string_span<>(vec)); } } - TEST(EnzureRemoveZ) + TEST(ConstrutorsEnsureZ) { // remove z from literals { @@ -351,6 +438,18 @@ SUITE(string_span_tests) } #endif + // default + { + cstring_span<> span; + CHECK(span.length() == 0); + } + + // from nullptr + { + cstring_span<> span(nullptr); + CHECK(span.length() == 0); + } + // from string literal { cstring_span<> span = "Hello"; @@ -378,14 +477,34 @@ SUITE(string_span_tests) CHECK(span.length() == 5); } - // from non-const ptr and length + // from const ptr and length, include 0 { - // does not compile with GCC (ISO standard does not allow converting string literals to char*) -#ifdef CONFIRM_COMPILATION_ERRORS - char* ptr = "Hello"; + const char* ptr = "Hello"; + cstring_span<> span{ ptr, 6 }; + CHECK(span.length() == 6); + } + + // from const ptr and length, 0 inside + { + const char* ptr = "He\0lo"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from non-const ptr and length + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from non-const ptr and length, 0 inside + { + char ar[] = { 'H', 'e', '\0', 'l', 'o' }; + char* ptr = ar; cstring_span<> span{ ptr, 5 }; CHECK(span.length() == 5); -#endif } // from const string From c6f3579ad1502015e320facf882f54caefbc266b Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Sat, 5 Dec 2015 01:03:19 +0000 Subject: [PATCH 2/4] Changes for gcc and clang --- include/string_span.h | 212 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 204 insertions(+), 8 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 8418029..6b0b32b 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -213,6 +213,7 @@ namespace details template class basic_string_span { +public: using value_type = CharT; using const_value_type = std::add_const_t; using pointer = std::add_pointer_t; @@ -221,7 +222,6 @@ class basic_string_span using bounds_type = static_bounds; using impl_type = span; -public: using size_type = ptrdiff_t; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; @@ -542,7 +542,7 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator==(const T& one, gsl::basic_string_span other) noexcept { @@ -550,6 +550,40 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + // operator != template one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator!=(const T& one, gsl::basic_string_span other) noexcept { return !(one == other); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} +#endif + // operator< template one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator<(const T& one, gsl::basic_string_span other) noexcept { @@ -592,6 +658,40 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + // operator <= template one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator<=(const T& one, gsl::basic_string_span other) noexcept { return !(other < one); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} +#endif + // operator> template (gsl::basic_string_span one, const T& other) noexce template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator>(const T& one, gsl::basic_string_span other) noexcept { return other < one; } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} +#endif + // operator >= template =(gsl::basic_string_span one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} +#endif + // VS 2013 workarounds #ifdef _MSC_VER From e19f6b5f362aac677b369b9e2b46010df13cf944 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 7 Dec 2015 15:22:11 -0800 Subject: [PATCH 3/4] Fixed a bug on creating a span from empty string --- include/string_span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_span.h b/include/string_span.h index 6b0b32b..d3419ee 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -270,7 +270,7 @@ public: // from string constexpr basic_string_span(std::string& s) noexcept - : span_(&(s.at(0)), narrow_cast(s.length())) + : span_(const_cast(s.data()), narrow_cast(s.length())) {} // from containers. Containers must have .size() and .data() function signatures From 5926942cd7b8270dc851297b27e995bc5d32ab7d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 7 Dec 2015 15:30:00 -0800 Subject: [PATCH 4/4] Tyding up comments --- include/string_span.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index d3419ee..a451314 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -552,8 +552,8 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexce #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template (const T& one, gsl::basic_string_span other) noexce #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template =(const T& one, gsl::basic_string_span other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template