diff --git a/include/string_span.h b/include/string_span.h index feb5ac6..23158ed 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -87,64 +87,70 @@ using wzstring = wchar_t*; // // 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) +template +span ensure_sentinel(T const* const seq, ptrdiff_t const max = PTRDIFF_MAX) { - auto cur = seq; - while ((cur - seq) < max && *cur != Sentinel) ++cur; - Ensures(*cur == Sentinel); - return{ seq, cur - seq }; + auto const it = std::find(seq, seq + max, Sentinal); + Ensures(*it == Sentinel); + return{ seq, it - 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) +namespace details { - return ensure_sentinel(sz, max); + inline ptrdiff_t length_func(char const* const sz, ptrdiff_t const len) noexcept + { + return narrow_cast(strnlen(sz, narrow_cast(len))); + } + + inline ptrdiff_t length_func(wchar_t const* const sz, ptrdiff_t const len) noexcept + { + return narrow_cast(wcsnlen(sz, narrow_cast(len))); + } + + template > + using is_char_t = std::bool_constant< + std::is_same::value || std::is_same::value>; + + template > + inline span ensure_z(T const sz, ptrdiff_t const max, std::true_type) + { + auto const len = length_func(sz, max); + Ensures(sz[len] == 0); + return{ sz, narrow_cast(len) }; + } + + // + // 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, ptrdiff_t const max, std::false_type) + { + 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) +template +inline span ensure_z(T* const& sz, ptrdiff_t const max = PTRDIFF_MAX) { - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return details::ensure_z(sz, max, details::is_char_t {}); } -inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +template +inline span ensure_z(T (&sz)[N]) { - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return details::ensure_z(sz, narrow_cast(N), details::is_char_t {}); } -inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +template +inline auto ensure_z(Cont& cont) -> span> { - auto len = wcsnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return ensure_z(cont.data(), static_cast(cont.size())); } -inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, narrow_cast(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 +span ensure_z(Cont&& cont) = delete; template class basic_string_span; @@ -162,46 +168,6 @@ namespace details 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, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(strnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; } @@ -431,7 +397,7 @@ private: static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) noexcept { - return{ sz, details::length_func()(sz, max)}; + return{ sz, details::length_func(sz, max)}; } template @@ -550,7 +516,7 @@ 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 +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators @@ -604,7 +570,7 @@ bool operator!=(const T& one, gsl::basic_string_span other) noexc return !(one == other); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators @@ -658,7 +624,7 @@ 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 +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators @@ -712,7 +678,7 @@ bool operator<=(const T& one, gsl::basic_string_span other) noexc return !(other < one); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators @@ -764,7 +730,7 @@ bool operator>(const T& one, gsl::basic_string_span other) noexce return other < one; } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators @@ -816,7 +782,7 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc return !(one < other); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 18fda26..ee1b81f 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -40,15 +40,39 @@ SUITE(string_span_tests) TEST(TestConstructFromStdString) { std::string s = "Hello there world"; + auto const len = static_cast::size_type>(s.length()); + cstring_span<> v = s; - CHECK(v.length() == static_cast::size_type>(s.length())); + CHECK(v.length() == len); + + s.back() = 0; + v = ensure_z(s); + CHECK(v.length() == len - 1); } TEST(TestConstructFromStdVector) { std::vector vec(5, 'h'); + auto const size = static_cast::size_type>(vec.size()); + string_span<> v = vec; - CHECK(v.length() == static_cast::size_type>(vec.size())); + CHECK(v.length() == size); + + vec.back() = 0; + v = ensure_z(vec); + CHECK(v.length() == size - 1); + } + + TEST(TestConstructFromStdArray) + { + std::array arr {"1234"}; + auto const size = static_cast::size_type>(arr.size()); + + string_span<> v = arr; + CHECK(v.length() == 5); + + v = ensure_z(arr); + CHECK(v.length() == 4); } TEST(TestStackArrayConstruction) @@ -264,7 +288,7 @@ SUITE(string_span_tests) string_span<> _span{ _ptr, 5 }; // non-const span, non-const other type - + CHECK(_span == _ar); CHECK(_span == _ar1); CHECK(_span == _ar2);