diff --git a/include/span.h b/include/span.h index 6ee810d..8151737 100644 --- a/include/span.h +++ b/include/span.h @@ -51,8 +51,8 @@ #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG #define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT -// noexcept is not understood -#ifndef GSL_THROWS_ON_CONTRACT_VIOLATION +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ #endif diff --git a/include/string_span.h b/include/string_span.h index 250c528..cc87068 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -20,24 +20,47 @@ #define GSL_STRING_SPAN_H #include "gsl_assert.h" +#include "gsl_util.h" #include "span.h" #include -// VS 2013 workarounds #ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +// VS 2013 workarounds #if _MSC_VER <= 1800 -#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif #endif // _MSC_VER <= 1800 #endif // _MSC_VER +// In order to test the library, we need it to throw exceptions that we can catch +#ifdef GSL_THROW_ON_CONTRACT_VIOLATION + +#ifdef _MSC_VER +#pragma push_macro("noexcept") +#endif + +#define noexcept /* nothing */ + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + namespace gsl { // // czstring and wzstring // -// These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays) +// These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays) // that allow static analysis to help find bugs. // // There are no additional features/semantics that we can find a way to add inside the @@ -56,13 +79,364 @@ using zstring = char*; template using wzstring = wchar_t*; +// +// ensure_sentinel() +// +// Provides a way to obtain an span from a contiguous sequence +// that ends with a (non-inclusive) sentinel value. +// +// Will fail-fast if sentinel cannot be found before max elements are examined. +// +template +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + Ensures(*cur == Sentinel); + return{ seq, cur - seq }; +} + + +// +// ensure_z - creates a span for a czstring or cwzstring. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// +template +inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline span ensure_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +template +span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } + +template +span::type, dynamic_range> ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), static_cast(cont.length())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + {}; + + template + struct is_basic_string_span_oracle> : std::true_type + {}; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + {}; + + template + struct length_func + {}; + + template <> + struct length_func + { + std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, length)); + } + }; +} + + // // string_span and relatives // // Note that Extent is always single-dimension only // -template -using basic_string_span = span; +template +class basic_string_span +{ + using value_type = CharT; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t; + 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; + using reverse_iterator = typename impl_type::reverse_iterator; + using const_reverse_iterator = typename impl_type::const_reverse_iterator; + + // default (empty) + constexpr basic_string_span() = default; + + // copy + constexpr basic_string_span(const basic_string_span& other) = default; + + // move + constexpr basic_string_span(basic_string_span&& other) = default; + + // assign + constexpr basic_string_span& operator=(const basic_string_span& other) = default; + + // move assign + constexpr basic_string_span& operator=(basic_string_span&& other) = default; + + // 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 and string literals + template + constexpr basic_string_span(value_type(&arr)[N]) noexcept + : span_(remove_z(arr)) + {} + + // Those allow 0s within the length, so we do not remove them + + // from string + constexpr basic_string_span(std::string& s) noexcept + : span_(&(s.at(0)), narrow_cast(s.length())) + {} + + // from containers. Containers must have .size() and .data() function signatures + template ::value + && !details::is_basic_string_span::value + && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + constexpr basic_string_span(Cont& cont) + : span_(cont.data(), cont.size()) + {} + + // disallow creation from temporary containers and strings + template ::value + && !details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + basic_string_span(Cont&& cont) = delete; + + // from span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(span other) noexcept + : span_(other) + {} + + // from string_span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(basic_string_span other) noexcept + : span_(other.data(), other.length()) + {} + + constexpr bool empty() const noexcept + { + return length() == 0; + } + + // first Count elements + template + constexpr basic_string_span first() const noexcept + { + return{ span_.template first() }; + } + + constexpr basic_string_span first(size_type count) const noexcept + { + return{ span_.first(count) }; + } + + // last Count elements + template + constexpr basic_string_span last() const noexcept + { + return{ span_.template last() }; + } + + constexpr basic_string_span last(size_type count) const noexcept + { + return{ span_.last(count) }; + } + + // create a subview of Count elements starting from Offset + template + constexpr basic_string_span subspan() const noexcept + { + return{ span_.template subspan() }; + } + + constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_range) const noexcept + { + return{ span_.subspan(offset, count) }; + } + + constexpr reference operator[](size_type idx) const noexcept + { + return span_[idx]; + } + + constexpr pointer data() const noexcept + { + return span_.data(); + } + + // length of the span in elements + constexpr size_type length() const noexcept + { + return span_.size(); + } + + // length of the span in elements + constexpr size_type size() const noexcept + { + return span_.size(); + } + + // length of the span in bytes + constexpr size_type size_bytes() const noexcept + { + return span_.size_bytes(); + } + + // length of the span in bytes + constexpr size_type length_bytes() const noexcept + { + return span_.length_bytes(); + } + + constexpr iterator begin() const noexcept + { + return span_.begin(); + } + + constexpr iterator end() const noexcept + { + return span_.end(); + } + + constexpr const_iterator cbegin() const noexcept + { + return span_.cbegin(); + } + + constexpr const_iterator cend() const noexcept + { + span_.cend(); + } + + constexpr reverse_iterator rbegin() const noexcept + { + return span_.rbegin(); + } + + constexpr reverse_iterator rend() const noexcept + { + return span_.rend(); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return span_.crbegin(); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return span_.crend(); + } + +private: + + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) noexcept + { + return{ sz, details::length_func()(sz, max)}; + } + + template + static impl_type remove_z(value_type(&sz)[N]) noexcept + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; +}; template using string_span = basic_string_span; @@ -76,77 +450,12 @@ using wstring_span = basic_string_span; template using cwstring_span = basic_string_span; - -// -// ensure_sentinel() -// -// Provides a way to obtain an span from a contiguous sequence -// that ends with a (non-inclusive) sentinel value. -// -// Will fail-fast if sentinel cannot be found before max elements are examined. -// -template -span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIFF_MAX) -{ - auto cur = seq; - while ((cur - seq) < max && *cur != Sentinel) ++cur; - Ensures(*cur == Sentinel); - return{ seq, cur - seq }; -} - - -// -// ensure_z - creates a string_span for a czstring or cwzstring. -// Will fail fast if a null-terminator cannot be found before -// the limit of size_type. -// -template -inline basic_string_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) -{ - return ensure_sentinel(sz, max); -} - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline basic_string_span ensure_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; -} - -inline basic_string_span ensure_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; -} - -inline basic_string_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; -} - -inline basic_string_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; -} - -template -basic_string_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } - -template -basic_string_span::type, dynamic_range> ensure_z(Cont& cont) -{ - return ensure_z(cont.data(), static_cast(cont.length())); -} - // // to_string() allow (explicit) conversions from string_span to string // -#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -template +template std::basic_string::type> to_string(basic_string_span view) { return{ view.data(), static_cast(view.length()) }; @@ -174,12 +483,13 @@ inline std::wstring to_string(wstring_span<> view) return{ view.data(), view.length() }; } -#endif +#endif -template +template class basic_zstring_builder { public: + using impl_type = span; using string_span_type = basic_string_span; using value_type = CharT; using pointer = CharT*; @@ -203,7 +513,7 @@ public: iterator end() const { return sv_.end(); } private: - string_span_type sv_; + impl_type sv_; }; template @@ -213,14 +523,64 @@ template using wzstring_builder = basic_zstring_builder; } +template +bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + +template +bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + 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 +{ + return !(other < one); +} + +template +bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return other < one; +} + +template +bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(one < other); +} + // VS 2013 workarounds #ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + #if _MSC_VER <= 1800 -#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#pragma warning(pop) + +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#undef noexcept +#pragma pop_macro("noexcept") +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #endif // _MSC_VER <= 1800 #endif // _MSC_VER +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION #endif // GSL_STRING_SPAN_H diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index f14df93..a966955 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -1,20 +1,21 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. +// /////////////////////////////////////////////////////////////////////////////// -#include + +#include #include #include #include @@ -27,7 +28,7 @@ SUITE(string_span_tests) { TEST(TestLiteralConstruction) - { + { cwstring_span<> v = ensure_z(L"Hello"); CHECK(5 == v.length()); @@ -35,7 +36,7 @@ SUITE(string_span_tests) #ifdef CONFIRM_COMPILATION_ERRORS wstring_span<> v2 = ensure0(L"Hello"); #endif - } + } TEST(TestConstructFromStdString) { @@ -51,8 +52,8 @@ SUITE(string_span_tests) CHECK(v.length() == static_cast::size_type>(vec.size())); } - TEST(TestStackArrayConstruction) - { + TEST(TestStackArrayConstruction) + { wchar_t stack_string[] = L"Hello"; { @@ -62,7 +63,7 @@ SUITE(string_span_tests) { cwstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } { @@ -72,9 +73,9 @@ SUITE(string_span_tests) { wstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } - } + } TEST(TestConstructFromConstCharPointer) { @@ -87,7 +88,7 @@ SUITE(string_span_tests) { char stack_string[] = "Hello"; string_span<> v = ensure_z(stack_string); - cstring_span<> v2 = v; + cstring_span<> v2 = v; CHECK(v.length() == v2.length()); } @@ -95,7 +96,7 @@ SUITE(string_span_tests) { char stack_string[] = "Hello"; cstring_span<> v = ensure_z(stack_string); - (void)v; + (void)v; #ifdef CONFIRM_COMPILATION_ERRORS string_span<> v2 = v; string_span<> v3 = "Hello"; @@ -113,6 +114,573 @@ SUITE(string_span_tests) CHECK(static_cast::size_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); } + + TEST(EqualityAndImplicitConstructors) + { + { + cstring_span<> span = "Hello"; + + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const char* ptr = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + // comparison to literal + CHECK(span == cstring_span<>("Hello")); + + // comparison to static array with no null termination + CHECK(span == cstring_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span == cstring_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span == cstring_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span == cstring_span<>(ptr, 5)); + + // comparison to string + CHECK(span == cstring_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span == cstring_span<>(vec)); + + // comparison of the original data to string + CHECK(span.data() == std::string("Hello")); + } + + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + + string_span<> span = ar; + + char ar1[] = "Hello"; + char ar2[10] = "Hello"; + char* ptr = ar; + std::string str = "Hello"; + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + // comparison to static array with no null termination + CHECK(span == string_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span == string_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span == string_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span == string_span<>(ptr, 5)); + + // comparison to string + CHECK(span == string_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span == string_span<>(vec)); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + cstring_span<> span = "Hello"; + + CHECK(span == "Hello"); + CHECK(span == ar); + CHECK(span == ar1); + CHECK(span == ar2); + CHECK(span == ptr); + CHECK(span == str); + CHECK(span == vec); + + char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char _ar1[] = "Hello"; + char _ar2[10] = "Hello"; + char* _ptr = _ar1; + std::string _str = "Hello"; + std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; + + CHECK(span == _ar); + CHECK(span == _ar1); + CHECK(span == _ar2); + CHECK(span == _ptr); + CHECK(span == _str); + CHECK(span == _vec); + + string_span<> _span{ _ptr }; + + CHECK(_span == _ar); + CHECK(_span == _ar1); + CHECK(_span == _ar2); + CHECK(_span == _ptr); + CHECK(_span == _str); + CHECK(_span == _vec); + + CHECK(_span == "Hello"); + CHECK(_span == ar); + CHECK(_span == ar1); + CHECK(_span == ar2); + CHECK(_span == ptr); + CHECK(_span == str); + CHECK(_span == vec); + } +#endif + + { + std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span1 = str1; + std::vector str2 = std::move(str1); + cstring_span<> span2 = str2; + + // comparison of spans from the same vector before and after move (ok) + CHECK(span1 == span2); + } + } + + + TEST(ComparisonAndImplicitConstructors) + { + { + cstring_span<> span = "Hello"; + + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + const char ar1[] = "Hello"; + const char ar2[10] = "Hello"; + const char* ptr = "Hello"; + const std::string str = "Hello"; + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + // comparison to literal + CHECK(span < cstring_span<>("Helloo")); + CHECK(span > cstring_span<>("Hell")); + + // comparison to static array with no null termination + CHECK(span >= cstring_span<>(ar)); + + // comparison to static array with null at the end + CHECK(span <= cstring_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span >= cstring_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span <= cstring_span<>(ptr, 5)); + + // comparison to string + CHECK(span >= cstring_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span <= cstring_span<>(vec)); + } + + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + + string_span<> span = ar; + + char larr[] = "Hell"; + char rarr[] = "Helloo"; + + char ar1[] = "Hello"; + char ar2[10] = "Hello"; + char* ptr = ar; + std::string str = "Hello"; + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + + + // comparison to static array with no null termination + CHECK(span <= string_span<>(ar)); + CHECK(span < string_span<>(rarr)); + CHECK(span > string_span<>(larr)); + + // comparison to static array with null at the end + CHECK(span >= string_span<>(ar1)); + + // comparison to static array with null in the middle + CHECK(span <= string_span<>(ar2)); + + // comparison to null-terminated c string + CHECK(span >= string_span<>(ptr, 5)); + + // comparison to string + CHECK(span <= string_span<>(str)); + + // comparison to vector of charaters with no null termination + CHECK(span >= string_span<>(vec)); + } + } + TEST(EnzureRemoveZ) + { + // remove z from literals + { + cstring_span<> sp = "hello"; + CHECK((sp.length() == 5)); + } + + // take the string as is + { + auto str = std::string("hello"); + cstring_span<> sp = str; + CHECK((sp.length() == 5)); + } + + // ensure z on c strings + { + char* ptr = new char[3]; + + ptr[0] = 'a'; + ptr[1] = 'b'; + ptr[2] = '\0'; + + string_span<> span = ensure_z(ptr); + CHECK(span.length() == 2); + + delete[] ptr; + } + } + + TEST(Constructors) + { + // creating cstring_span + + // from string temporary +#ifdef CONFIRM_COMPILATION_ERRORS + { + cstring_span<> span = std::string("Hello"); + } +#endif + + // from string literal + { + cstring_span<> span = "Hello"; + CHECK(span.length() == 5); + } + + // from const static array + { + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = ar; + CHECK(span.length() == 5); + } + + // from non-const static array + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = ar; + CHECK(span.length() == 5); + } + + // from const ptr and length + { + const char* ptr = "Hello"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from non-const ptr and length + { + // does not compile with GCC (ISO standard does not allow converting string literals to char*) +#ifdef CONFIRM_COMPILATION_ERRORS + char* ptr = "Hello"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); +#endif + } + + // from const string + { + const std::string str = "Hello"; + cstring_span<> span = str; + CHECK(span.length() == 5); + } + + // from non-const string + { + std::string str = "Hello"; + cstring_span<> span = str; + CHECK(span.length() == 5); + } + + // from const vector + { + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> tmp = vec; + cstring_span<> span = tmp; + CHECK(span.length() == 5); + } + + // from non-const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + cstring_span<> span = tmp; + CHECK(span.length() == 5); + } + + // creating string_span + + // from string literal + { +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> span = "Hello"; +#endif + } + + // from const static array + { +#ifdef CONFIRM_COMPILATION_ERRORS + const char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = ar; + CHECK(span.length() == 5); +#endif + } + + // from non-const static array + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = ar; + CHECK(span.length() == 5); + } + + // from const ptr and length + { +#ifdef CONFIRM_COMPILATION_ERRORS + const char* ptr = "Hello"; + string_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); +#endif + } + + // from non-const ptr and length + { + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; + string_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // from const string + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::string str = "Hello"; + string_span<> span = str; + CHECK(span.length() == 5); +#endif + } + + // from non-const string + { + std::string str = "Hello"; + string_span<> span = str; + CHECK(span.length() == 5); + } + + // from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); +#endif + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span of non-const data from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from const string_span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); +#endif + } + + // from non-const string_span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); + } + + // from non-const string_span from const vector + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); +#endif + } + + // from const string_span of non-const data + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const string_span<> tmp = vec; + string_span<> span = tmp; + CHECK(span.length() == 5); + } + } + + template + T move_wrapper(T&& t) + { + return std::move(t); + } + + template + T create() { return T{}; } + + template + void use(basic_string_span s) {} + + TEST(MoveConstructors) + { + // move string_span + { + cstring_span<> span = "Hello"; + auto span1 = std::move(span); + CHECK(span1.length() == 5); + } + { + cstring_span<> span = "Hello"; + auto span1 = move_wrapper(std::move(span)); + CHECK(span1.length() == 5); + } + { + cstring_span<> span = "Hello"; + auto span1 = move_wrapper(std::move(span)); + CHECK(span1.length() == 5); + } + + // move span + { + span span = ensure_z("Hello"); + cstring_span<> span1 = std::move(span); + CHECK(span1.length() == 5); + } + { + span span = ensure_z("Hello"); + cstring_span<> span2 = move_wrapper(std::move(span)); + CHECK(span2.length() == 5); + } + + // move string + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::string str = "Hello"; + string_span<> span = std::move(str); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::string str = "Hello"; + string_span<> span = move_wrapper(std::move(str)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create()); +#endif + } + + // move container + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = std::move(vec); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = move_wrapper>(std::move(vec)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create>()); +#endif + } + } + + TEST(Conversion) + { +#ifdef CONFIRM_COMPPILATION_ERRORS + cstring_span<> span = "Hello"; + cwstring_span<> wspan{ span }; + CHECK(wspan.length() == 5); +#endif + } } int main(int, const char *[])