From e3878a655658d8198fa9ad5ea9df7ad9705bd6db Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 30 Nov 2015 12:24:00 -0800 Subject: [PATCH 1/5] merging with master --- include/span.h | 4 +- include/string_span.h | 433 +++++++++++++++++++++++++++++++++--- tests/string_span_tests.cpp | 401 ++++++++++++++++++++++++++++++++- 3 files changed, 798 insertions(+), 40 deletions(-) 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..b11dd9e 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -23,15 +23,37 @@ #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 +// 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 { // @@ -56,27 +78,6 @@ using zstring = char*; template using wzstring = wchar_t*; -// -// string_span and relatives -// -// Note that Extent is always single-dimension only -// -template -using basic_string_span = span; - -template -using string_span = basic_string_span; - -template -using cstring_span = basic_string_span; - -template -using wstring_span = basic_string_span; - -template -using cwstring_span = basic_string_span; - - // // ensure_sentinel() // @@ -86,7 +87,7 @@ using cwstring_span = basic_string_span; // 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) +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; @@ -101,46 +102,341 @@ span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIF // the limit of size_type. // template -inline basic_string_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +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 basic_string_span ensure_z(char* const& sz, std::ptrdiff_t max) +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 basic_string_span ensure_z(const char* const& sz, std::ptrdiff_t max) +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 basic_string_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +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 basic_string_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +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 -basic_string_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } +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) +span::type, dynamic_range> ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline span remove_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + return{ sz, static_cast(len) }; +} + +template +span remove_z(T(&sz)[N]) +{ + return remove_z(&sz[0], static_cast(N)); +} + +template +span::type, dynamic_range> remove_z(Cont& cont) +{ + return remove_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> + {}; +} + +// +// string_span and relatives +// +// Note that Extent is always single-dimension only +// +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 underlying_type = span; + +public: + using size_type = ptrdiff_t; + using iterator = typename underlying_type::iterator; + using const_iterator = typename underlying_type::const_iterator; + using reverse_iterator = typename underlying_type::reverse_iterator; + using const_reverse_iterator = typename underlying_type::const_reverse_iterator; + + // empty + constexpr basic_string_span() noexcept + : real(nullptr) + {} + + // copy + constexpr basic_string_span(const basic_string_span& other) noexcept + : real(other.real) + {} + + // move + constexpr basic_string_span(const basic_string_span&& other) noexcept + : real(std::move(other.real)) + {} + + // from nullptr and length + constexpr basic_string_span(nullptr_t ptr, size_type length) noexcept + : real(ptr, length) + {} + + // For pointers and static arrays - if 0-terminated, remove 0 from the view + + // from c string + + constexpr basic_string_span(pointer& ptr) noexcept + : real(ensure_z(ptr)) + {} + + // from non-const pointer to const span + template::value>> + constexpr basic_string_span(std::remove_const_t*& ptr) noexcept + : real(ensure_z(ptr)) + {} + + // from raw data and length - remove 0 if needed + constexpr basic_string_span(pointer ptr, size_type length) noexcept + : real(remove_z(ptr, length)) + {} + + // from static arrays and string literals + template + constexpr basic_string_span(value_type(&arr)[N]) noexcept + : real(remove_z(arr)) + {} + + // Those allow 0s in the middle, so we keep them + + constexpr basic_string_span(std::string& s) noexcept + : real(&(s.at(0)), static_cast(s.length())) + {} + + // from containers. It 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) + : real(cont.data(), cont.size()) + {} + + // from span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(const span& other) noexcept + : real(other) + {} + + // from string_span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(const basic_string_span& other) noexcept + : real(other.data(), other.length()) + {} + + // section on linear space + template + constexpr basic_string_span first() const noexcept + { + return{ real.first() }; + } + + constexpr basic_string_span first(size_type count) const noexcept + { + return{ real.first(count); } + } + + template + constexpr basic_string_span last() const noexcept + { + return{ real.last() }; + } + + constexpr basic_string_span last(size_type count) const noexcept + { + return{ real.last(count); } + } + + template + constexpr basic_string_span sub() const noexcept + { + return{ real.sub() }; + } + + constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept + { + return{ real.sub(offset, count) }; + } + + constexpr const_reference operator[](size_type idx) const noexcept + { + return real[idx]; + } + + constexpr reference operator[](size_type idx) noexcept + { + return real[idx]; + } + + constexpr pointer data() const noexcept + { + return real.data(); + } + + constexpr size_type length() const noexcept + { + return real.size(); + } + + constexpr size_type size() const noexcept + { + return real.size(); + } + + constexpr size_type used_length() const noexcept + { + return length(); + } + + constexpr size_type bytes() const noexcept + { + return real.bytes(); + } + + constexpr size_type used_bytes() const noexcept + { + return bytes(); + } + + constexpr explicit operator bool() const noexcept + { + return real; + } + + constexpr iterator begin() const noexcept + { + return real.begin(); + } + + constexpr iterator end() const noexcept + { + return real.end(); + } + + constexpr const_iterator cbegin() const noexcept + { + return real.cbegin(); + } + + constexpr const_iterator cend() const noexcept + { + real.cend(); + } + + constexpr reverse_iterator rbegin() const noexcept + { + return real.rbegin(); + } + + constexpr reverse_iterator rend() const noexcept + { + return real.rend(); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return real.crbegin(); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return real.crend(); + } + +private: + span real; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + // // to_string() allow (explicit) conversions from string_span to string // @@ -213,14 +509,89 @@ template using wzstring_builder = basic_zstring_builder; } + +constexpr bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + +constexpr bool operator==(const gsl::string_span<>& one, const gsl::string_span<>& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + + +// TODO: ca we make twmplate ops work? +//template +//constexpr 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 , std::remove_cv_t>::value>> +constexpr 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 , std::remove_cv_t>::value>> +constexpr bool operator!=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(one == other); +} + +template , std::remove_cv_t>::value>> +constexpr 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 , std::remove_cv_t>::value>> +constexpr bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(other < one); +} + +template , std::remove_cv_t>::value>> +constexpr bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return other < one; +} + +template , std::remove_cv_t>::value>> +constexpr 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..08faaa8 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include using namespace std; using namespace gsl; @@ -27,7 +29,7 @@ SUITE(string_span_tests) { TEST(TestLiteralConstruction) - { + { cwstring_span<> v = ensure_z(L"Hello"); CHECK(5 == v.length()); @@ -35,7 +37,7 @@ SUITE(string_span_tests) #ifdef CONFIRM_COMPILATION_ERRORS wstring_span<> v2 = ensure0(L"Hello"); #endif - } + } TEST(TestConstructFromStdString) { @@ -51,8 +53,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 +64,7 @@ SUITE(string_span_tests) { cwstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } { @@ -72,7 +74,7 @@ SUITE(string_span_tests) { wstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } } @@ -95,7 +97,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 +115,391 @@ SUITE(string_span_tests) CHECK(static_cast::size_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); } + + 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<>("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")); + + 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); + } + + { + 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(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(ptr); + CHECK(span.length() == 2); + + delete[] ptr; + } + + // ensuze z on c strings + { + char* ptr = new char[2]; + + ptr[0] = 'a'; + ptr[1] = 'b'; + + // do we want to have a constructor from pointer at all? + // the behavior is unpredictable if the string is not 0-terminated + + // CHECK_THROW((string_span<>(ptr).length() == 2), fail_fast); + + cstring_span<> sp1{ ptr, 2 }; // good + cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there + + CHECK(sp1[1] == 'b'); + CHECK_THROW((sp1[2] == 'c'), fail_fast); + + CHECK(sp2[1] == 'b'); + //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow + + delete[] ptr; + } + } + + TEST(Constructors) + { + // 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 + { + char* ptr = "Hello"; + cstring_span<> span{ ptr, 5 }; + CHECK(span.length() == 5); + } + + // 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); + } + + /////////////////////////////////////////////////// + // How string_span should behave with const data + + // from string literal + { +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> span = "Hello"; + CHECK(span.length() == 5); +#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* ptr = "Hello"; + 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 (looks like a bug) + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; // fix error (happens inside the constructor) + 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; // what does "const span" mean? + string_span<> span = tmp; + CHECK(span.length() == 5); + } + } + } int main(int, const char *[]) From 8c5d06dc791c7dea8148a5d3867473fab2e2fcf3 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 23 Nov 2015 21:24:08 +0000 Subject: [PATCH 2/5] fixed GCC and clang compilation issues --- include/string_span.h | 58 +++++++++++++++++++++---------------- tests/string_span_tests.cpp | 8 ++++- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b11dd9e..b681a5c 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -86,7 +86,7 @@ using wzstring = wchar_t*; // // Will fail-fast if sentinel cannot be found before max elements are examined. // -template +template span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; @@ -101,7 +101,7 @@ span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) // Will fail fast if a null-terminator cannot be found before // the limit of size_type. // -template +template inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); @@ -133,7 +133,7 @@ inline span ensure_z(const wchar_t* const& sz, std Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -template +template span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template @@ -168,7 +168,7 @@ inline span remove_z(const wchar_t* const& sz, std return{ sz, static_cast(len) }; } -template +template span remove_z(T(&sz)[N]) { return remove_z(&sz[0], static_cast(N)); @@ -203,7 +203,7 @@ namespace details // // Note that Extent is always single-dimension only // -template +template class basic_string_span { using value_type = CharT; @@ -237,7 +237,7 @@ public: {} // from nullptr and length - constexpr basic_string_span(nullptr_t ptr, size_type length) noexcept + constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept : real(ptr, length) {} @@ -250,8 +250,8 @@ public: {} // from non-const pointer to const span - template::value>> - constexpr basic_string_span(std::remove_const_t*& ptr) noexcept + template, bool Enabled = std::is_const::value, typename Dummy = std::enable_if_t> + constexpr basic_string_span(ValueType*& ptr) noexcept : real(ensure_z(ptr)) {} @@ -306,29 +306,29 @@ public: template constexpr basic_string_span first() const noexcept { - return{ real.first() }; + return{ real.template first() }; } constexpr basic_string_span first(size_type count) const noexcept { - return{ real.first(count); } + return{ real.first(count) }; } template constexpr basic_string_span last() const noexcept { - return{ real.last() }; + return{ real.template last() }; } constexpr basic_string_span last(size_type count) const noexcept { - return{ real.last(count); } + return{ real.last(count) }; } template constexpr basic_string_span sub() const noexcept { - return{ real.sub() }; + return{ real.template sub() }; } constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept @@ -442,7 +442,7 @@ using cwstring_span = basic_string_span; // #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()) }; @@ -472,7 +472,7 @@ inline std::wstring to_string(wstring_span<> view) #endif -template +template class basic_zstring_builder { public: @@ -509,24 +509,32 @@ template using wzstring_builder = basic_zstring_builder; } - -constexpr bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept +/* +bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } -constexpr bool operator==(const gsl::string_span<>& one, const gsl::string_span<>& other) noexcept +bool operator==(const gsl::cwstring_span<>& one, const gsl::cwstring_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::equal(one.begin(), one.end(), other.begin(), other.end()); } - -// TODO: ca we make twmplate ops work? -//template -//constexpr 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 , std::remove_cv_t>::value>> +constexpr 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 , std::remove_cv_t>::value>> constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 08faaa8..90b7c3c 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -248,7 +248,7 @@ SUITE(string_span_tests) cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there CHECK(sp1[1] == 'b'); - CHECK_THROW((sp1[2] == 'c'), fail_fast); + CHECK_THROW((void)(sp1[2] == 'c'), fail_fast); CHECK(sp2[1] == 'b'); //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow @@ -295,9 +295,12 @@ SUITE(string_span_tests) // 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 @@ -398,9 +401,12 @@ SUITE(string_span_tests) // from non-const ptr and length { + // does not compile with GCC (ISO standard does not allows converting string literals to char*) +#ifdef CONFIRM_COMPILATION_ERRORS char* ptr = "Hello"; string_span<> span{ ptr, 5 }; CHECK(span.length() == 5); +#endif } // from const string From 7077105b9de356b67695fe6b5d7ec0af2ec9e107 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 23 Nov 2015 23:05:31 -0800 Subject: [PATCH 3/5] Added operator== --- include/string_span.h | 336 +++++++++++++++++------------------- tests/string_span_tests.cpp | 268 +++++++++++++++++++++++----- 2 files changed, 375 insertions(+), 229 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b681a5c..b6dce6b 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -20,6 +20,7 @@ #define GSL_STRING_SPAN_H #include "gsl_assert.h" +#include "gsl_util.h" #include "span.h" #include @@ -32,12 +33,12 @@ // 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 +// noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /* nothing */ #endif #endif // _MSC_VER <= 1800 @@ -50,16 +51,16 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ +#define noexcept /* nothing */ -#endif // GSL_THROW_ON_CONTRACT_VIOLATION +#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 @@ -79,7 +80,7 @@ template using wzstring = wchar_t*; // -// ensure_sentinel() +// ensure_sentinel() // // Provides a way to obtain an span from a contiguous sequence // that ends with a (non-inclusive) sentinel value. @@ -97,7 +98,7 @@ span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) // -// ensure_z - creates a string_span for a czstring or cwzstring. +// 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. // @@ -118,19 +119,22 @@ inline span ensure_z(char* const& sz, std::ptrdiff_t max) 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) }; + 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) }; + 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) }; + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; } template @@ -142,45 +146,7 @@ span::type, dynamic_range> return ensure_z(cont.data(), static_cast(cont.length())); } - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline span remove_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - return{ sz, static_cast(len) }; -} - -template -span remove_z(T(&sz)[N]) -{ - return remove_z(&sz[0], static_cast(N)); -} - -template -span::type, dynamic_range> remove_z(Cont& cont) -{ - return remove_z(cont.data(), static_cast(cont.length())); -} - -template +template class basic_string_span; namespace details @@ -189,15 +155,56 @@ namespace details 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_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 // @@ -212,85 +219,83 @@ class basic_string_span using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t; using bounds_type = static_bounds; - using underlying_type = span; + using impl_type = span; public: using size_type = ptrdiff_t; - using iterator = typename underlying_type::iterator; - using const_iterator = typename underlying_type::const_iterator; - using reverse_iterator = typename underlying_type::reverse_iterator; - using const_reverse_iterator = typename underlying_type::const_reverse_iterator; + 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; - // empty - constexpr basic_string_span() noexcept - : real(nullptr) - {} + // default (empty) + constexpr basic_string_span() = default; // copy - constexpr basic_string_span(const basic_string_span& other) noexcept - : real(other.real) - {} + constexpr basic_string_span(const basic_string_span& other) = default; // move - constexpr basic_string_span(const basic_string_span&& other) noexcept - : real(std::move(other.real)) - {} + 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 - : real(ptr, length) + : span_(ptr, length) {} // For pointers and static arrays - if 0-terminated, remove 0 from the view - // from c string - - constexpr basic_string_span(pointer& ptr) noexcept - : real(ensure_z(ptr)) - {} - - // from non-const pointer to const span - template, bool Enabled = std::is_const::value, typename Dummy = std::enable_if_t> - constexpr basic_string_span(ValueType*& ptr) noexcept - : real(ensure_z(ptr)) - {} - - // from raw data and length - remove 0 if needed + // from raw data and length constexpr basic_string_span(pointer ptr, size_type length) noexcept - : real(remove_z(ptr, length)) + : span_(remove_z(ptr, length)) {} // from static arrays and string literals template constexpr basic_string_span(value_type(&arr)[N]) noexcept - : real(remove_z(arr)) + : span_(remove_z(arr)) {} - // Those allow 0s in the middle, so we keep them + // Those allow 0s within the length, so we do not remove them + // from string constexpr basic_string_span(std::string& s) noexcept - : real(&(s.at(0)), static_cast(s.length())) + : span_(&(s.at(0)), narrow_cast(s.length())) {} - // from containers. It must have .size() and .data() function signatures + // from containers. Containers must have .size() and .data() function signatures template ::value - && !details::is_basic_string_span::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) - : real(cont.data(), cont.size()) + : 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(const span& other) noexcept - : real(other) + constexpr basic_string_span(span other) noexcept + : span_(other) {} // from string_span @@ -298,131 +303,130 @@ public: typename OtherBounds = static_bounds, typename Dummy = std::enable_if_t::value && std::is_convertible::value> > - constexpr basic_string_span(const basic_string_span& other) noexcept - : real(other.data(), other.length()) + constexpr basic_string_span(basic_string_span other) noexcept + : span_(other.data(), other.length()) {} - // section on linear space + constexpr bool empty() const noexcept + { + return length() == 0; + } + + // first Count elements template constexpr basic_string_span first() const noexcept { - return{ real.template first() }; + return{ span_.template first() }; } constexpr basic_string_span first(size_type count) const noexcept { - return{ real.first(count) }; + return{ span_.first(count) }; } + // last Count elements template constexpr basic_string_span last() const noexcept { - return{ real.template last() }; + return{ span_.template last() }; } constexpr basic_string_span last(size_type count) const noexcept { - return{ real.last(count) }; + return{ span_.last(count) }; } + // Count elements starting from Offset template constexpr basic_string_span sub() const noexcept { - return{ real.template sub() }; + return{ span_.template sub() }; } constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept { - return{ real.sub(offset, count) }; + return{ span_.sub(offset, count) }; } - constexpr const_reference operator[](size_type idx) const noexcept + constexpr reference operator[](size_type idx) const noexcept { - return real[idx]; - } - - constexpr reference operator[](size_type idx) noexcept - { - return real[idx]; + return span_[idx]; } constexpr pointer data() const noexcept { - return real.data(); + return span_.data(); } constexpr size_type length() const noexcept { - return real.size(); + return span_.size(); } constexpr size_type size() const noexcept { - return real.size(); - } - - constexpr size_type used_length() const noexcept - { - return length(); + return span_.size(); } constexpr size_type bytes() const noexcept { - return real.bytes(); - } - - constexpr size_type used_bytes() const noexcept - { - return bytes(); - } - - constexpr explicit operator bool() const noexcept - { - return real; + return span_.bytes(); } constexpr iterator begin() const noexcept { - return real.begin(); + return span_.begin(); } constexpr iterator end() const noexcept { - return real.end(); + return span_.end(); } constexpr const_iterator cbegin() const noexcept { - return real.cbegin(); + return span_.cbegin(); } constexpr const_iterator cend() const noexcept { - real.cend(); + span_.cend(); } constexpr reverse_iterator rbegin() const noexcept { - return real.rbegin(); + return span_.rbegin(); } constexpr reverse_iterator rend() const noexcept { - return real.rend(); + return span_.rend(); } constexpr const_reverse_iterator crbegin() const noexcept { - return real.crbegin(); + return span_.crbegin(); } constexpr const_reverse_iterator crend() const noexcept { - return real.crend(); + return span_.crend(); } private: - span real; + + 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 @@ -440,7 +444,7 @@ using cwstring_span = basic_string_span; // // 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 std::basic_string::type> to_string(basic_string_span view) @@ -470,12 +474,13 @@ inline std::wstring to_string(wstring_span<> view) return{ view.data(), view.length() }; } -#endif +#endif template class basic_zstring_builder { public: + using impl_type = span; using string_span_type = basic_string_span; using value_type = CharT; using pointer = CharT*; @@ -499,7 +504,7 @@ public: iterator end() const { return sv_.end(); } private: - string_span_type sv_; + impl_type sv_; }; template @@ -509,69 +514,36 @@ template using wzstring_builder = basic_zstring_builder; } -/* -bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept +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()); } -bool operator==(const gsl::cwstring_span<>& one, const gsl::cwstring_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::equal(one.begin(), one.end(), other.begin(), other.end()); -} - -/* -template , std::remove_cv_t>::value>> -constexpr 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 , std::remove_cv_t>::value>> -constexpr 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 , std::remove_cv_t>::value>> -constexpr bool operator!=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept -{ - return !(one == other); -} - -template , std::remove_cv_t>::value>> -constexpr bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +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 , std::remove_cv_t>::value>> -constexpr bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(other < one); } -template , std::remove_cv_t>::value>> -constexpr bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return other < one; } -template , std::remove_cv_t>::value>> -constexpr bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +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 @@ -592,7 +564,7 @@ constexpr bool operator>=(const gsl::basic_string_span& one, #endif // _MSC_VER <= 1800 #endif // _MSC_VER -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #undef noexcept diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 90b7c3c..4e42cc5 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -14,12 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// -#include +#include #include #include #include -#include -#include using namespace std; using namespace gsl; @@ -76,7 +74,7 @@ SUITE(string_span_tests) wstring_span<> v = stack_string; CHECK(v.length() == 5); } - } + } TEST(TestConstructFromConstCharPointer) { @@ -89,7 +87,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()); } @@ -116,7 +114,7 @@ SUITE(string_span_tests) CHECK(s2.length() == 5); } - TEST(ComparisonAndImplicitConstructors) + TEST(EqualityAndImplicitConstructors) { { cstring_span<> span = "Hello"; @@ -132,25 +130,60 @@ SUITE(string_span_tests) CHECK(span == cstring_span<>("Hello")); // comparison to static array with no null termination - CHECK(span == cstring_span<>(ar)); + 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)); + CHECK(span == cstring_span<>(ar2)); // comparison to null-terminated c string - CHECK(span == cstring_span<>(ptr, 5)); + CHECK(span == cstring_span<>(ptr, 5)); // comparison to string - CHECK(span == cstring_span<>(str)); + CHECK(span == cstring_span<>(str)); // comparison to vector of charaters with no null termination - CHECK(span == cstring_span<>(vec)); + 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); @@ -159,7 +192,7 @@ SUITE(string_span_tests) CHECK(span == ptr); CHECK(span == str); CHECK(span == vec); - + char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; char _ar1[] = "Hello"; char _ar2[10] = "Hello"; @@ -191,6 +224,7 @@ SUITE(string_span_tests) CHECK(_span == str); CHECK(_span == vec); } +#endif { std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; @@ -203,6 +237,78 @@ SUITE(string_span_tests) } } + + 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 @@ -226,39 +332,17 @@ SUITE(string_span_tests) ptr[1] = 'b'; ptr[2] = '\0'; - string_span<> span(ptr); + string_span<> span = ensure_z(ptr); CHECK(span.length() == 2); delete[] ptr; } - - // ensuze z on c strings - { - char* ptr = new char[2]; - - ptr[0] = 'a'; - ptr[1] = 'b'; - - // do we want to have a constructor from pointer at all? - // the behavior is unpredictable if the string is not 0-terminated - - // CHECK_THROW((string_span<>(ptr).length() == 2), fail_fast); - - cstring_span<> sp1{ ptr, 2 }; // good - cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there - - CHECK(sp1[1] == 'b'); - CHECK_THROW((void)(sp1[2] == 'c'), fail_fast); - - CHECK(sp2[1] == 'b'); - //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow - - delete[] ptr; - } } TEST(Constructors) { + // creating cstring_span + // from string temporary #ifdef CONFIRM_COMPILATION_ERRORS { @@ -363,14 +447,12 @@ SUITE(string_span_tests) CHECK(span.length() == 5); } - /////////////////////////////////////////////////// - // How string_span should behave with const data + // creating string_span // from string literal { #ifdef CONFIRM_COMPILATION_ERRORS string_span<> span = "Hello"; - CHECK(span.length() == 5); #endif } @@ -401,12 +483,10 @@ SUITE(string_span_tests) // from non-const ptr and length { - // does not compile with GCC (ISO standard does not allows converting string literals to char*) -#ifdef CONFIRM_COMPILATION_ERRORS - char* ptr = "Hello"; + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; string_span<> span{ ptr, 5 }; CHECK(span.length() == 5); -#endif } // from const string @@ -459,11 +539,11 @@ SUITE(string_span_tests) CHECK(span.length() == 5); } - // from non-const span of non-const data from const vector (looks like a bug) + // 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; // fix error (happens inside the constructor) + const span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -500,12 +580,106 @@ SUITE(string_span_tests) // from const string_span of non-const data { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const string_span<> tmp = vec; // what does "const span" mean? + 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 *[]) From de6dbacbf6debb96297f4c554473069b8a3edb4c Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 1 Dec 2015 13:52:01 -0800 Subject: [PATCH 4/5] Addressed CR comments --- include/string_span.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b6dce6b..cc87068 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -336,16 +336,16 @@ public: return{ span_.last(count) }; } - // Count elements starting from Offset + // create a subview of Count elements starting from Offset template - constexpr basic_string_span sub() const noexcept + constexpr basic_string_span subspan() const noexcept { - return{ span_.template sub() }; + return{ span_.template subspan() }; } - constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept + constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_range) const noexcept { - return{ span_.sub(offset, count) }; + return{ span_.subspan(offset, count) }; } constexpr reference operator[](size_type idx) const noexcept @@ -358,19 +358,28 @@ public: 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(); } - constexpr size_type bytes() const noexcept + // length of the span in bytes + constexpr size_type size_bytes() const noexcept { - return span_.bytes(); + 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 From 4efa9e8f07fbe5799444c8464bd6061403dce9ef Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 2 Dec 2015 12:28:43 -0800 Subject: [PATCH 5/5] Run clang format --- tests/string_span_tests.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 4e42cc5..a966955 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -1,18 +1,19 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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