diff --git a/include/string_span.h b/include/string_span.h index ecbee7c..46bf2d4 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -71,17 +71,21 @@ namespace gsl // type system for these types that will not either incur significant runtime costs or // (sometimes needlessly) break existing programs when introduced. // -template -using czstring = const char*; + +template +using basic_zstring = CharT*; template -using cwzstring = const wchar_t*; +using czstring = basic_zstring; template -using zstring = char*; +using cwzstring = basic_zstring; template -using wzstring = wchar_t*; +using zstring = basic_zstring; + +template +using wzstring = basic_zstring; // // ensure_sentinel() @@ -521,43 +525,82 @@ inline std::wstring to_string(wstring_span<> view) #endif -template -class basic_zstring_builder +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings +template +class basic_zstring_span { public: - using impl_type = span; - using string_span_type = basic_string_span; using value_type = CharT; - using pointer = CharT*; - using size_type = typename string_span_type::size_type; - using iterator = typename string_span_type::iterator; + using const_value_type = std::add_const_t; - basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {} + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; - template - basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {} + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; - pointer data() const { return sv_.data(); } - string_span_type view() const { return sv_; } + using impl_type = span; + using string_span_type = basic_string_span; - size_type length() const { return sv_.length(); } + constexpr basic_zstring_span(impl_type span) noexcept + : span_(span) + { + // expects a zero-terminated span + Expects(span[span.size() - 1] == '\0'); + } - pointer assume0() const { return data(); } - string_span_type ensure_z() const { return gsl::ensure_z(sv_); } + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; - iterator begin() const { return sv_.begin(); } - iterator end() const { return sv_.end(); } + // move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span(basic_zstring_span&& other) + : span_(std::move(other.span_)) + {} +#endif + + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; + + // move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + constexpr bool empty() const noexcept { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const noexcept { return span_.first(span_.size()-1); } + + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } private: - impl_type sv_; + impl_type span_; }; -template -using zstring_builder = basic_zstring_builder; +template +using zstring_span = basic_zstring_span; -template -using wzstring_builder = basic_zstring_builder; -} +template +using wzstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +} // namespace GSL // operator == template CreateTempName(string_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = 't'; + span[1] = 'm'; + span[2] = 'p'; + last = 3; + } + span[last] = '\0'; + + auto ret = span.subspan(0, 4); + return{ ret }; + } + + TEST(zstring) + { + + // create zspan from zero terminated string + { + char buf[1]; + buf[0] = '\0'; + + zstring_span<> zspan({ buf, 1 }); + + CHECK(strlen(zspan.assume_z()) == 0); + CHECK(zspan.as_string_span().size() == 0); + CHECK(zspan.ensure_z().size() == 0); + } + + // create zspan from non-zero terminated string + { + char buf[1]; + buf[0] = 'a'; + + auto workaround_macro = [&]() { zstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + char buf[10]; + + auto name = CreateTempName({ buf, 10 }); + if (!name.empty()) + { + czstring<> str = name.assume_z(); + CHECK(strlen(str) == 3); + CHECK(*(str+3) == '\0'); + } + } + + } + + cwzstring_span<> CreateTempNameW(wstring_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = L't'; + span[1] = L'm'; + span[2] = L'p'; + last = 3; + } + span[last] = L'\0'; + + auto ret = span.subspan(0, 4); + return{ ret }; + } + + TEST(wzstring) + { + + // create zspan from zero terminated string + { + wchar_t buf[1]; + buf[0] = L'\0'; + + wzstring_span<> zspan({ buf, 1 }); + + CHECK(wcsnlen(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 + { + wchar_t buf[1]; + buf[0] = L'a'; + + auto workaround_macro = [&]() { wzstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + wchar_t buf[10]; + + auto name = CreateTempNameW({ buf, 10 }); + if (!name.empty()) + { + cwzstring<> str = name.assume_z(); + CHECK(wcsnlen(str, 10) == 3); + CHECK(*(str + 3) == L'\0'); + } + } + + } } int main(int, const char *[])