Made string_span details::string_length() generic (Fix issue #542) (#543)

* Made string_span details::string_length() generic

removed overloads & specialized classes

Creating string_spans using `char16_t` and `char32_t` was not possible
without creating new specializations and function overloads.

This patch makes details::string_length() generic removing the need to
extend the overloads and specializations.

* added type aliases for string_span types char16_t and char32_t

* Added char16_t & char32_t overloads for ensure_z

* added string_span tests for char16_T & char32_t

* added zstring type aliases for char16_t & char32_t

* Added tests for char16_t & char31_t zstring and string_span types

* applies clang format to <gsl/string_span>

* Clang format tests/string_span_tests.cpp

* Removed ensure_z() overloads as they don't add functionality.
This commit is contained in:
Galik 2017-09-18 23:20:51 +01:00 committed by Neil MacIntosh
parent e7bcdf541b
commit 4c5fdb541f
2 changed files with 265 additions and 103 deletions

View File

@ -71,31 +71,32 @@ using czstring = basic_zstring<const char, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent> template <std::ptrdiff_t Extent = dynamic_extent>
using cwzstring = basic_zstring<const wchar_t, Extent>; using cwzstring = basic_zstring<const wchar_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using cu16zstring = basic_zstring<const char16_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using cu32zstring = basic_zstring<const char32_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent> template <std::ptrdiff_t Extent = dynamic_extent>
using zstring = basic_zstring<char, Extent>; using zstring = basic_zstring<char, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent> template <std::ptrdiff_t Extent = dynamic_extent>
using wzstring = basic_zstring<wchar_t, Extent>; using wzstring = basic_zstring<wchar_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using u16zstring = basic_zstring<char16_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using u32zstring = basic_zstring<char32_t, Extent>;
namespace details namespace details
{ {
inline std::ptrdiff_t string_length(const char* str, std::ptrdiff_t n) template <class CharT>
std::ptrdiff_t string_length(const CharT* str, std::ptrdiff_t n)
{ {
if (str == nullptr || n <= 0) return 0; if (str == nullptr || n <= 0) return 0;
span<const char> str_span{str, n}; span<const CharT> str_span{str, n};
std::ptrdiff_t len = 0;
while (len < n && str_span[len]) len++;
return len;
}
inline std::ptrdiff_t wstring_length(const wchar_t* str, std::ptrdiff_t n)
{
if (str == nullptr || n <= 0) return 0;
span<const wchar_t> str_span{str, n};
std::ptrdiff_t len = 0; std::ptrdiff_t len = 0;
while (len < n && str_span[len]) len++; while (len < n && str_span[len]) len++;
@ -122,48 +123,18 @@ span<T, dynamic_extent> ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX
} }
// //
// ensure_z - creates a span for a czstring or cwzstring. // ensure_z - creates a span for a zero terminated strings.
// Will fail fast if a null-terminator cannot be found before // Will fail fast if a null-terminator cannot be found before
// the limit of size_type. // the limit of size_type.
// //
template <typename T> template <typename CharT>
inline span<T, dynamic_extent> ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) inline span<CharT, dynamic_extent> ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRDIFF_MAX)
{ {
return ensure_sentinel<T, 0>(sz, max); return ensure_sentinel<CharT, CharT(0)>(sz, max);
} }
// TODO (neilmac) there is probably a better template-magic way to get the const and non-const template <typename CharT, std::size_t N>
// overloads to share an implementation span<CharT, dynamic_extent> ensure_z(CharT (&sz)[N])
inline span<char, dynamic_extent> ensure_z(char* const& sz, std::ptrdiff_t max)
{
auto len = details::string_length(sz, max);
Ensures(sz[len] == 0);
return {sz, len};
}
inline span<const char, dynamic_extent> ensure_z(const char* const& sz, std::ptrdiff_t max)
{
auto len = details::string_length(sz, max);
Ensures(sz[len] == 0);
return {sz, len};
}
inline span<wchar_t, dynamic_extent> ensure_z(wchar_t* const& sz, std::ptrdiff_t max)
{
auto len = details::wstring_length(sz, max);
Ensures(sz[len] == 0);
return {sz, len};
}
inline span<const wchar_t, dynamic_extent> ensure_z(const wchar_t* const& sz, std::ptrdiff_t max)
{
auto len = details::wstring_length(sz, max);
Ensures(sz[len] == 0);
return {sz, len};
}
template <typename T, std::size_t N>
span<T, dynamic_extent> ensure_z(T (&sz)[N])
{ {
return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N)); return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N));
} }
@ -194,47 +165,6 @@ namespace details
struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>> struct is_basic_string_span : is_basic_string_span_oracle<std::remove_cv_t<T>>
{ {
}; };
template <typename T>
struct length_func
{
};
template <>
struct length_func<char>
{
std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
{
return details::string_length(ptr, length);
}
};
template <>
struct length_func<wchar_t>
{
std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
{
return details::wstring_length(ptr, length);
}
};
template <>
struct length_func<const char>
{
std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
{
return details::string_length(ptr, length);
}
};
template <>
struct length_func<const wchar_t>
{
std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) GSL_NOEXCEPT
{
return details::wstring_length(ptr, length);
}
};
} }
// //
@ -399,7 +329,7 @@ public:
private: private:
static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) static impl_type remove_z(pointer const& sz, std::ptrdiff_t max)
{ {
return {sz, details::length_func<element_type>()(sz, max)}; return {sz, details::string_length(sz, max)};
} }
template <std::size_t N> template <std::size_t N>
@ -423,6 +353,18 @@ using wstring_span = basic_string_span<wchar_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent> template <std::ptrdiff_t Extent = dynamic_extent>
using cwstring_span = basic_string_span<const wchar_t, Extent>; using cwstring_span = basic_string_span<const wchar_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using u16string_span = basic_string_span<char16_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using cu16string_span = basic_string_span<const char16_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using u32string_span = basic_string_span<char32_t, Extent>;
template <std::ptrdiff_t Extent = dynamic_extent>
using cu32string_span = basic_string_span<const char32_t, Extent>;
// //
// to_string() allow (explicit) conversions from string_span to string // to_string() allow (explicit) conversions from string_span to string
// //
@ -499,12 +441,24 @@ using zstring_span = basic_zstring_span<char, Max>;
template <std::ptrdiff_t Max = dynamic_extent> template <std::ptrdiff_t Max = dynamic_extent>
using wzstring_span = basic_zstring_span<wchar_t, Max>; using wzstring_span = basic_zstring_span<wchar_t, Max>;
template <std::ptrdiff_t Max = dynamic_extent>
using u16zstring_span = basic_zstring_span<char16_t, Max>;
template <std::ptrdiff_t Max = dynamic_extent>
using u32zstring_span = basic_zstring_span<char32_t, Max>;
template <std::ptrdiff_t Max = dynamic_extent> template <std::ptrdiff_t Max = dynamic_extent>
using czstring_span = basic_zstring_span<const char, Max>; using czstring_span = basic_zstring_span<const char, Max>;
template <std::ptrdiff_t Max = dynamic_extent> template <std::ptrdiff_t Max = dynamic_extent>
using cwzstring_span = basic_zstring_span<const wchar_t, Max>; using cwzstring_span = basic_zstring_span<const wchar_t, Max>;
template <std::ptrdiff_t Max = dynamic_extent>
using cu16zstring_span = basic_zstring_span<const char16_t, Max>;
template <std::ptrdiff_t Max = dynamic_extent>
using cu32zstring_span = basic_zstring_span<const char32_t, Max>;
// operator == // operator ==
template <class CharT, std::ptrdiff_t Extent, class T, template <class CharT, std::ptrdiff_t Extent, class T,
class = std::enable_if_t< class = std::enable_if_t<

View File

@ -19,6 +19,7 @@
#include <gsl/gsl> //owner #include <gsl/gsl> //owner
#include <gsl/string_span> #include <gsl/string_span>
#include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <map> #include <map>
#include <vector> #include <vector>
@ -26,6 +27,27 @@
using namespace std; using namespace std;
using namespace gsl; using namespace gsl;
// Generic string functions
namespace generic
{
template <typename CharT>
auto strlen(const CharT* s)
{
auto p = s;
while (*p) ++p;
return p - s;
}
template <typename CharT>
auto strnlen(const CharT* s, std::size_t n)
{
return std::find(s, s + n, CharT(0)) - s;
}
} // namespace generic
TEST_CASE("TestLiteralConstruction") TEST_CASE("TestLiteralConstruction")
{ {
cwstring_span<> v = ensure_z(L"Hello"); cwstring_span<> v = ensure_z(L"Hello");
@ -874,7 +896,7 @@ TEST_CASE("zstring")
zstring_span<> zspan({buf, 1}); zstring_span<> zspan({buf, 1});
CHECK(strlen(zspan.assume_z()) == 0); CHECK(generic::strlen(zspan.assume_z()) == 0);
CHECK(zspan.as_string_span().size() == 0); CHECK(zspan.as_string_span().size() == 0);
CHECK(zspan.ensure_z().size() == 0); CHECK(zspan.ensure_z().size() == 0);
} }
@ -895,7 +917,7 @@ TEST_CASE("zstring")
auto name = CreateTempName({buf, 10}); auto name = CreateTempName({buf, 10});
if (!name.empty()) { if (!name.empty()) {
czstring<> str = name.assume_z(); czstring<> str = name.assume_z();
CHECK(strlen(str) == 3); CHECK(generic::strlen(str) == 3);
CHECK(*(str + 3) == '\0'); CHECK(*(str + 3) == '\0');
} }
} }
@ -928,7 +950,7 @@ TEST_CASE("wzstring")
wzstring_span<> zspan({buf, 1}); wzstring_span<> zspan({buf, 1});
CHECK(wcsnlen(zspan.assume_z(), 1) == 0); CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
CHECK(zspan.as_string_span().size() == 0); CHECK(zspan.as_string_span().size() == 0);
CHECK(zspan.ensure_z().size() == 0); CHECK(zspan.ensure_z().size() == 0);
} }
@ -949,7 +971,115 @@ TEST_CASE("wzstring")
const auto name = CreateTempNameW({buf, 10}); const auto name = CreateTempNameW({buf, 10});
if (!name.empty()) { if (!name.empty()) {
cwzstring<> str = name.assume_z(); cwzstring<> str = name.assume_z();
CHECK(wcsnlen(str, 10) == 3); CHECK(generic::strnlen(str, 10) == 3);
CHECK(*(str + 3) == L'\0');
}
}
}
cu16zstring_span<> CreateTempNameU16(u16string_span<> span)
{
Expects(span.size() > 1);
int last = 0;
if (span.size() > 4) {
span[0] = u't';
span[1] = u'm';
span[2] = u'p';
last = 3;
}
span[last] = u'\0';
auto ret = span.subspan(0, 4);
return {ret};
}
TEST_CASE("u16zstring")
{
// create zspan from zero terminated string
{
char16_t buf[1];
buf[0] = L'\0';
u16zstring_span<> zspan({buf, 1});
CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
CHECK(zspan.as_string_span().size() == 0);
CHECK(zspan.ensure_z().size() == 0);
}
// create zspan from non-zero terminated string
{
char16_t buf[1];
buf[0] = u'a';
const auto workaround_macro = [&]() { u16zstring_span<> zspan({buf, 1}); };
CHECK_THROWS_AS(workaround_macro(), fail_fast);
}
// usage scenario: create zero-terminated temp file name and pass to a legacy API
{
char16_t buf[10];
const auto name = CreateTempNameU16({buf, 10});
if (!name.empty()) {
cu16zstring<> str = name.assume_z();
CHECK(generic::strnlen(str, 10) == 3);
CHECK(*(str + 3) == L'\0');
}
}
}
cu32zstring_span<> CreateTempNameU32(u32string_span<> span)
{
Expects(span.size() > 1);
int last = 0;
if (span.size() > 4) {
span[0] = U't';
span[1] = U'm';
span[2] = U'p';
last = 3;
}
span[last] = U'\0';
auto ret = span.subspan(0, 4);
return {ret};
}
TEST_CASE("u32zstring")
{
// create zspan from zero terminated string
{
char32_t buf[1];
buf[0] = L'\0';
u32zstring_span<> zspan({buf, 1});
CHECK(generic::strnlen(zspan.assume_z(), 1) == 0);
CHECK(zspan.as_string_span().size() == 0);
CHECK(zspan.ensure_z().size() == 0);
}
// create zspan from non-zero terminated string
{
char32_t buf[1];
buf[0] = u'a';
const auto workaround_macro = [&]() { u32zstring_span<> zspan({buf, 1}); };
CHECK_THROWS_AS(workaround_macro(), fail_fast);
}
// usage scenario: create zero-terminated temp file name and pass to a legacy API
{
char32_t buf[10];
const auto name = CreateTempNameU32({buf, 10});
if (!name.empty()) {
cu32zstring<> str = name.assume_z();
CHECK(generic::strnlen(str, 10) == 3);
CHECK(*(str + 3) == L'\0'); CHECK(*(str + 3) == L'\0');
} }
} }
@ -961,3 +1091,81 @@ TEST_CASE("Issue305")
CHECK(foo["foo"] == 0); CHECK(foo["foo"] == 0);
CHECK(foo["bar"] == 1); CHECK(foo["bar"] == 1);
} }
TEST_CASE("char16_t type")
{
gsl::cu16string_span<> ss1 = gsl::ensure_z(u"abc");
CHECK(ss1.size() == 3);
CHECK(ss1.size_bytes() == 6);
std::u16string s1 = gsl::to_string(ss1);
CHECK(s1 == u"abc");
std::u16string s2 = u"abc";
gsl::u16string_span<> ss2 = s2;
CHECK(ss2.size() == 3);
gsl::u16string_span<> ss3 = ss2.subspan(1, 1);
CHECK(ss3.size() == 1);
CHECK(ss3[0] == u'b');
char16_t buf[4]{u'a', u'b', u'c', u'\0'};
gsl::u16string_span<> ss4{buf, 4};
CHECK(ss4[3] == u'\0');
gsl::cu16zstring_span<> ss5(u"abc");
CHECK(ss5.as_string_span().size() == 3);
gsl::cu16string_span<> ss6 = ss5.as_string_span();
CHECK(ss6 == ss1);
std::vector<char16_t> v7 = {u'a', u'b', u'c'};
gsl::cu16string_span<> ss7{v7};
CHECK(ss7 == ss1);
gsl::cu16string_span<> ss8 = gsl::ensure_z(u"abc");
gsl::cu16string_span<> ss9 = gsl::ensure_z(u"abc");
CHECK(ss8 == ss9);
ss9 = gsl::ensure_z(u"abd");
CHECK(ss8 < ss9);
CHECK(ss8 <= ss9);
CHECK(ss8 != ss9);
}
TEST_CASE("char32_t type")
{
gsl::cu32string_span<> ss1 = gsl::ensure_z(U"abc");
CHECK(ss1.size() == 3);
CHECK(ss1.size_bytes() == 12);
std::u32string s1 = gsl::to_string(ss1);
CHECK(s1 == U"abc");
std::u32string s2 = U"abc";
gsl::u32string_span<> ss2 = s2;
CHECK(ss2.size() == 3);
gsl::u32string_span<> ss3 = ss2.subspan(1, 1);
CHECK(ss3.size() == 1);
CHECK(ss3[0] == U'b');
char32_t buf[4]{U'a', U'b', U'c', U'\0'};
gsl::u32string_span<> ss4{buf, 4};
CHECK(ss4[3] == u'\0');
gsl::cu32zstring_span<> ss5(U"abc");
CHECK(ss5.as_string_span().size() == 3);
gsl::cu32string_span<> ss6 = ss5.as_string_span();
CHECK(ss6 == ss1);
gsl::cu32string_span<> ss8 = gsl::ensure_z(U"abc");
gsl::cu32string_span<> ss9 = gsl::ensure_z(U"abc");
CHECK(ss8 == ss9);
ss9 = gsl::ensure_z(U"abd");
CHECK(ss8 < ss9);
CHECK(ss8 <= ss9);
CHECK(ss8 != ss9);
}