Merge pull request #2 from Microsoft/master

Update from Microsoft/GSL
This commit is contained in:
Zachary Henkel 2015-12-09 13:05:01 -08:00
commit 004f0aba3b
17 changed files with 6600 additions and 4419 deletions

View File

@ -4,11 +4,11 @@ The Guidelines Support Library (GSL) contains functions and types that are sugge
[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org).
This repo contains Microsoft's implementation of GSL. This repo contains Microsoft's implementation of GSL.
The library includes types like `array_view<>`, `string_view<>`, `owner<>` and others. The library includes types like `span<T>`, `string_span`, `owner<>` and others.
The entire implementation is provided inline in the headers under the [include](./include) directory. The entire implementation is provided inline in the headers under the [include](./include) directory. The implementation generally assumes a platform that implements C++14 support. There are specific workarounds to support MSVC 2013 and 2015.
While some types have been broken out into their own headers (e.g. [include/array_view.h](./include/array_view.h)), While some types have been broken out into their own headers (e.g. [include/span.h](./include/span.h)),
it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library. it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library.
> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to > NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to

File diff suppressed because it is too large Load Diff

View File

@ -1,55 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef GSL_FAIL_FAST_H
#define GSL_FAIL_FAST_H
#include <exception>
#if defined(GSL_THROWS_FOR_TESTING)
#include <stdexcept>
#endif
namespace gsl
{
//
// Having "fail fast" result in an exception makes unit testing
// the GSL classes that rely upon it much simpler.
//
#if defined(GSL_THROWS_FOR_TESTING)
struct fail_fast : public std::runtime_error
{
fail_fast() : std::runtime_error("") {}
explicit fail_fast(char const* const message) : std::runtime_error(message) {}
};
inline void fail_fast_assert(bool cond) { if (!cond) throw fail_fast(); }
inline void fail_fast_assert(bool cond, const char* const message) { if (!cond) throw fail_fast(message); }
#else
inline void fail_fast_assert(bool cond) { if (!cond) std::terminate(); }
inline void fail_fast_assert(bool cond, const char* const) { if (!cond) std::terminate(); }
#endif // GSL_THROWS_FOR_TESTING
}
#endif // GSL_FAIL_FAST_H

View File

@ -19,23 +19,23 @@
#ifndef GSL_GSL_H #ifndef GSL_GSL_H
#define GSL_GSL_H #define GSL_GSL_H
#include "array_view.h" // array_view, strided_array_view... #include "gsl_assert.h" // Ensures/Expects
#include "string_view.h" // zstring, string_view, zstring_builder... #include "gsl_util.h" // finally()/narrow()/narrow_cast()...
#include "span.h" // span, strided_span...
#include "string_span.h" // zstring, string_span, zstring_builder...
#include <memory> #include <memory>
#ifdef _MSC_VER #ifdef _MSC_VER
// No MSVC does constexpr fully yet // No MSVC does constexpr fully yet
#pragma push_macro("constexpr") #pragma push_macro("constexpr")
#define constexpr /* nothing */ #define constexpr
// MSVC 2013 workarounds // MSVC 2013 workarounds
#if _MSC_VER <= 1800 #if _MSC_VER <= 1800
// noexcept is not understood // noexcept is not understood
#ifndef GSL_THROWS_FOR_TESTING #pragma push_macro("noexcept")
#define noexcept /* nothing */ #define noexcept
#endif
// turn off some misguided warnings // turn off some misguided warnings
#pragma warning(push) #pragma warning(push)
@ -45,11 +45,6 @@
#endif // _MSC_VER #endif // _MSC_VER
// In order to test the library, we need it to throw exceptions that we can catch
#ifdef GSL_THROWS_FOR_TESTING
#define noexcept /* nothing */
#endif // GSL_THROWS_FOR_TESTING
namespace gsl namespace gsl
{ {
@ -63,62 +58,6 @@ using std::shared_ptr;
template <class T> template <class T>
using owner = T; using owner = T;
//
// GSL.assert: assertions
//
#define Expects(x) gsl::fail_fast_assert((x))
#define Ensures(x) gsl::fail_fast_assert((x))
//
// GSL.util: utilities
//
// final_act allows you to ensure something gets run at the end of a scope
template <class F>
class final_act
{
public:
explicit final_act(F f) noexcept : f_(std::move(f)), invoke_(true) {}
final_act(final_act&& other) noexcept : f_(std::move(other.f_)), invoke_(other.invoke_) { other.invoke_ = false; }
final_act(const final_act&) = delete;
final_act& operator=(const final_act&) = delete;
~final_act() noexcept { if (invoke_) f_(); }
private:
F f_;
bool invoke_;
};
// finally() - convenience function to generate a final_act
template <class F>
final_act<F> finally(const F &f) noexcept { return final_act<F>(f); }
template <class F>
final_act<F> finally(F &&f) noexcept { return final_act<F>(std::forward<F>(f)); }
// narrow_cast(): a searchable way to do narrowing casts of values
template<class T, class U>
T narrow_cast(U u) noexcept { return static_cast<T>(u); }
struct narrowing_error : public std::exception {};
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template<class T, class U>
T narrow(U u) { T t = narrow_cast<T>(u); if (static_cast<U>(t) != u) throw narrowing_error(); return t; }
//
// at() - Bounds-checked way of accessing static arrays, std::array, std::vector
//
template <class T, size_t N>
T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; }
template <class T, size_t N>
T& at(std::array<T, N>& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; }
template <class Cont>
typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; }
// //
// not_null // not_null
@ -181,7 +120,7 @@ private:
// we assume that the compiler can hoist/prove away most of the checks inlined from this function // we assume that the compiler can hoist/prove away most of the checks inlined from this function
// if not, we could make them optional via conditional compilation // if not, we could make them optional via conditional compilation
void ensure_invariant() const { fail_fast_assert(ptr_ != nullptr); } void ensure_invariant() const { Expects(ptr_ != nullptr); }
// unwanted operators...pointers only point to single objects! // unwanted operators...pointers only point to single objects!
// TODO ensure all arithmetic ops on this type are unavailable // TODO ensure all arithmetic ops on this type are unavailable
@ -216,18 +155,14 @@ namespace std
#pragma pop_macro("constexpr") #pragma pop_macro("constexpr")
#if _MSC_VER <= 1800 #if _MSC_VER <= 1800
#pragma warning(pop)
#ifndef GSL_THROWS_FOR_TESTING #undef noexcept
#pragma undef noexcept #pragma pop_macro("noexcept")
#endif // GSL_THROWS_FOR_TESTING
#pragma warning(pop)
#endif // _MSC_VER <= 1800 #endif // _MSC_VER <= 1800
#endif // _MSC_VER #endif // _MSC_VER
#if defined(GSL_THROWS_FOR_TESTING)
#undef noexcept
#endif // GSL_THROWS_FOR_TESTING
#endif // GSL_GSL_H #endif // GSL_GSL_H

78
include/gsl_assert.h Normal file
View File

@ -0,0 +1,78 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef GSL_CONTRACTS_H
#define GSL_CONTRACTS_H
#include <exception>
//
// There are three configuration options for this GSL implementation's behavior
// when pre/post conditions on the GSL types are violated:
//
// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default)
// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown
// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens
//
#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) ^ defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) ^ defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION))
#define GSL_TERMINATE_ON_CONTRACT_VIOLATION
#endif
#define GSL_STRINGIFY_DETAIL(x) #x
#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x)
//
// GSL.assert: assertions
//
#if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
#include <stdexcept>
namespace gsl
{
struct fail_fast : public std::runtime_error
{
explicit fail_fast(char const* const message) : std::runtime_error(message) {}
};
}
#define Expects(cond) if (!(cond)) \
throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__));
#define Ensures(cond) if (!(cond)) \
throw gsl::fail_fast("GSL: Postcondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__));
#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION)
#define Expects(cond) if (!(cond)) std::terminate();
#define Ensures(cond) if (!(cond)) std::terminate();
#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)
#define Expects(cond)
#define Ensures(cond)
#endif
#endif // GSL_CONTRACTS_H

132
include/gsl_util.h Normal file
View File

@ -0,0 +1,132 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef GSL_UTIL_H
#define GSL_UTIL_H
#include "gsl_assert.h" // Ensures/Expects
#include <array>
#include <utility>
#include <exception>
#ifdef _MSC_VER
// No MSVC does constexpr fully yet
#pragma push_macro("constexpr")
#define constexpr
// MSVC 2013 workarounds
#if _MSC_VER <= 1800
// noexcept is not understood
#pragma push_macro("noexcept")
#define noexcept
// turn off some misguided warnings
#pragma warning(push)
#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior
#endif // _MSC_VER <= 1800
#endif // _MSC_VER
namespace gsl
{
//
// GSL.util: utilities
//
// final_act allows you to ensure something gets run at the end of a scope
template <class F>
class final_act
{
public:
explicit final_act(F f) noexcept
: f_(std::move(f)), invoke_(true)
{}
final_act(final_act&& other) noexcept
: f_(std::move(other.f_)), invoke_(other.invoke_)
{ other.invoke_ = false; }
final_act(const final_act&) = delete;
final_act& operator=(const final_act&) = delete;
~final_act() noexcept { if (invoke_) f_(); }
private:
F f_;
bool invoke_;
};
// finally() - convenience function to generate a final_act
template <class F>
inline final_act<F> finally(const F &f)
noexcept { return final_act<F>(f); }
template <class F>
inline final_act<F> finally(F &&f) noexcept
{ return final_act<F>(std::forward<F>(f)); }
// narrow_cast(): a searchable way to do narrowing casts of values
template<class T, class U>
inline constexpr T narrow_cast(U u) noexcept
{ return static_cast<T>(u); }
struct narrowing_error : public std::exception {};
// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
template<class T, class U>
inline T narrow(U u)
{ T t = narrow_cast<T>(u); if (static_cast<U>(t) != u) throw narrowing_error(); return t; }
//
// at() - Bounds-checked way of accessing static arrays, std::array, std::vector
//
template <class T, size_t N>
constexpr T& at(T(&arr)[N], size_t index)
{ Expects(index < N); return arr[index]; }
template <class T, size_t N>
constexpr T& at(std::array<T, N>& arr, size_t index)
{ Expects(index < N); return arr[index]; }
template <class Cont>
constexpr typename Cont::value_type& at(Cont& cont, size_t index)
{ Expects(index < cont.size()); return cont[index]; }
} // namespace gsl
#ifdef _MSC_VER
#undef constexpr
#pragma pop_macro("constexpr")
#if _MSC_VER <= 1800
#undef noexcept
#pragma pop_macro("noexcept")
#pragma warning(pop)
#endif // _MSC_VER <= 1800
#endif // _MSC_VER
#endif // GSL_UTIL_H

2224
include/span.h Normal file

File diff suppressed because it is too large Load Diff

881
include/string_span.h Normal file
View File

@ -0,0 +1,881 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef GSL_STRING_SPAN_H
#define GSL_STRING_SPAN_H
#include "gsl_assert.h"
#include "gsl_util.h"
#include "span.h"
#include <cstring>
#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
{
//
// czstring and wzstring
//
// 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
// type system for these types that will not either incur significant runtime costs or
// (sometimes needlessly) break existing programs when introduced.
//
template<std::ptrdiff_t Extent = dynamic_range>
using czstring = const char*;
template<std::ptrdiff_t Extent = dynamic_range>
using cwzstring = const wchar_t*;
template<std::ptrdiff_t Extent = dynamic_range>
using zstring = char*;
template<std::ptrdiff_t Extent = dynamic_range>
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<typename T, const T Sentinel>
span<T, dynamic_range> 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<typename T>
inline span<T, dynamic_range> ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX)
{
return ensure_sentinel<T, 0>(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<char, dynamic_range> ensure_z(char* const& sz, std::ptrdiff_t max)
{
auto len = strnlen(sz, max);
Ensures(sz[len] == 0);
return{ sz, static_cast<std::ptrdiff_t>(len) };
}
inline span<const char, dynamic_range> ensure_z(const char* const& sz, std::ptrdiff_t max)
{
auto len = strnlen(sz, max);
Ensures(sz[len] == 0);
return{ sz, static_cast<std::ptrdiff_t>(len) };
}
inline span<wchar_t, dynamic_range> ensure_z(wchar_t* const& sz, std::ptrdiff_t max)
{
auto len = wcsnlen(sz, max);
Ensures(sz[len] == 0);
return{ sz, static_cast<std::ptrdiff_t>(len) };
}
inline span<const wchar_t, dynamic_range> ensure_z(const wchar_t* const& sz, std::ptrdiff_t max)
{
auto len = wcsnlen(sz, max);
Ensures(sz[len] == 0);
return{ sz, static_cast<std::ptrdiff_t>(len) };
}
template<typename T, size_t N>
span<T, dynamic_range> ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast<std::ptrdiff_t>(N)); }
template<class Cont>
span<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_range> ensure_z(Cont& cont)
{
return ensure_z(cont.data(), static_cast<std::ptrdiff_t>(cont.length()));
}
template<typename CharT, std::ptrdiff_t>
class basic_string_span;
namespace details
{
template <typename T>
struct is_basic_string_span_oracle : std::false_type
{};
template <typename CharT, std::ptrdiff_t Extent>
struct is_basic_string_span_oracle<basic_string_span<CharT, Extent>> : std::true_type
{};
template <typename 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()(char* const ptr, std::ptrdiff_t length) noexcept
{
return narrow_cast<std::ptrdiff_t>(strnlen(ptr, length));
}
};
template <>
struct length_func<wchar_t>
{
std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept
{
return narrow_cast<std::ptrdiff_t>(wcsnlen(ptr, length));
}
};
template <>
struct length_func<const char>
{
std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept
{
return narrow_cast<std::ptrdiff_t>(strnlen(ptr, length));
}
};
template <>
struct length_func<const wchar_t>
{
std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept
{
return narrow_cast<std::ptrdiff_t>(wcsnlen(ptr, length));
}
};
}
//
// string_span and relatives
//
// Note that Extent is always single-dimension only
//
template <typename CharT, std::ptrdiff_t Extent = dynamic_range>
class basic_string_span
{
public:
using value_type = CharT;
using const_value_type = std::add_const_t<value_type>;
using pointer = std::add_pointer_t<value_type>;
using reference = std::add_lvalue_reference_t<value_type>;
using const_reference = std::add_lvalue_reference_t<const_value_type>;
using bounds_type = static_bounds<Extent>;
using impl_type = span<value_type, Extent>;
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
constexpr basic_string_span(std::nullptr_t ptr) noexcept
: span_(ptr)
{}
// from nullptr and length
constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept
: span_(ptr, length)
{}
// From static arrays - if 0-terminated, remove 0 from the view
// from static arrays and string literals
template<size_t N>
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 raw data and length
constexpr basic_string_span(pointer ptr, size_type length) noexcept
: span_(ptr, length)
{}
// from string
constexpr basic_string_span(std::string& s) noexcept
: span_(const_cast<pointer>(s.data()), narrow_cast<std::ptrdiff_t>(s.length()))
{}
// from containers. Containers must have .size() and .data() function signatures
template <typename Cont, typename DataType = typename Cont::value_type,
typename Dummy = std::enable_if_t<!details::is_span<Cont>::value
&& !details::is_basic_string_span<Cont>::value
&& !(!std::is_const<value_type>::value && std::is_const<Cont>::value) // no converting const containers to non-const span
&& std::is_convertible<DataType*, value_type*>::value
&& std::is_same<std::decay_t<decltype(std::declval<Cont>().size(), *std::declval<Cont>().data())>, DataType>::value>
>
constexpr basic_string_span(Cont& cont)
: span_(cont.data(), cont.size())
{}
// disallow creation from temporary containers and strings
template <typename Cont, typename DataType = typename Cont::value_type,
typename Dummy = std::enable_if_t<!details::is_span<Cont>::value
&& !details::is_basic_string_span<Cont>::value
&& std::is_convertible<DataType*, value_type*>::value
&& std::is_same<std::decay_t<decltype(std::declval<Cont>().size(), *std::declval<Cont>().data())>, DataType>::value>
>
basic_string_span(Cont&& cont) = delete;
// from span
template <typename OtherValueType, std::ptrdiff_t OtherExtent,
typename OtherBounds = static_bounds<OtherExtent>,
typename Dummy = std::enable_if_t<std::is_convertible<OtherValueType*, value_type*>::value && std::is_convertible<OtherBounds, bounds_type>::value>
>
constexpr basic_string_span(span<OtherValueType, OtherExtent> other) noexcept
: span_(other)
{}
// from string_span
template <typename OtherValueType, std::ptrdiff_t OtherExtent,
typename OtherBounds = static_bounds<OtherExtent>,
typename Dummy = std::enable_if_t<std::is_convertible<OtherValueType*, value_type*>::value && std::is_convertible<OtherBounds, bounds_type>::value>
>
constexpr basic_string_span(basic_string_span<OtherValueType, OtherExtent> other) noexcept
: span_(other.data(), other.length())
{}
constexpr bool empty() const noexcept
{
return length() == 0;
}
// first Count elements
template<size_type Count>
constexpr basic_string_span<value_type, Count> first() const noexcept
{
return{ span_.template first<Count>() };
}
constexpr basic_string_span<value_type, dynamic_range> first(size_type count) const noexcept
{
return{ span_.first(count) };
}
// last Count elements
template<size_type Count>
constexpr basic_string_span<value_type, Count> last() const noexcept
{
return{ span_.template last<Count>() };
}
constexpr basic_string_span<value_type, dynamic_range> last(size_type count) const noexcept
{
return{ span_.last(count) };
}
// create a subview of Count elements starting from Offset
template<size_type Offset, size_type Count>
constexpr basic_string_span<value_type, Count> subspan() const noexcept
{
return{ span_.template subspan<Offset, Count>() };
}
constexpr basic_string_span<value_type, dynamic_range> 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<value_type>()(sz, max)};
}
template<size_t N>
static impl_type remove_z(value_type(&sz)[N]) noexcept
{
return remove_z(&sz[0], narrow_cast<std::ptrdiff_t>(N));
}
impl_type span_;
};
template<std::ptrdiff_t Extent = dynamic_range>
using string_span = basic_string_span<char, Extent>;
template<std::ptrdiff_t Extent = dynamic_range>
using cstring_span = basic_string_span<const char, Extent>;
template<std::ptrdiff_t Extent = dynamic_range>
using wstring_span = basic_string_span<wchar_t, Extent>;
template<std::ptrdiff_t Extent = dynamic_range>
using cwstring_span = basic_string_span<const wchar_t, Extent>;
//
// to_string() allow (explicit) conversions from string_span to string
//
#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG
template<typename CharT, ptrdiff_t Extent>
std::basic_string<typename std::remove_const<CharT>::type> to_string(basic_string_span<CharT, Extent> view)
{
return{ view.data(), static_cast<size_t>(view.length()) };
}
#else
inline std::string to_string(cstring_span<> view)
{
return{ view.data(), view.length() };
}
inline std::string to_string(string_span<> view)
{
return{ view.data(), view.length() };
}
inline std::wstring to_string(cwstring_span<> view)
{
return{ view.data(), view.length() };
}
inline std::wstring to_string(wstring_span<> view)
{
return{ view.data(), view.length() };
}
#endif
template<typename CharT, size_t Extent = dynamic_range>
class basic_zstring_builder
{
public:
using impl_type = span<CharT, Extent>;
using string_span_type = basic_string_span<CharT, Extent>;
using value_type = CharT;
using pointer = CharT*;
using size_type = typename string_span_type::size_type;
using iterator = typename string_span_type::iterator;
basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {}
template<size_t Size>
basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {}
pointer data() const { return sv_.data(); }
string_span_type view() const { return sv_; }
size_type length() const { return sv_.length(); }
pointer assume0() const { return data(); }
string_span_type ensure_z() const { return gsl::ensure_z(sv_); }
iterator begin() const { return sv_.begin(); }
iterator end() const { return sv_.end(); }
private:
impl_type sv_;
};
template <size_t Max = dynamic_range>
using zstring_builder = basic_zstring_builder<char, Max>;
template <size_t Max = dynamic_range>
using wzstring_builder = basic_zstring_builder<wchar_t, Max>;
}
// operator ==
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator==(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator==(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator==(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end());
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator==(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end());
}
#endif
// operator !=
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(one == other);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(one == other);
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator!=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(one == other);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator!=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(one == other);
}
#endif
// operator<
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator<(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(other);
return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end());
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator<(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
gsl::basic_string_span<std::add_const_t<CharT>, Extent> tmp(one);
return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end());
}
#endif
// operator <=
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(other < one);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(other < one);
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator<=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(other < one);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator<=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(other < one);
}
#endif
// operator>
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return other < one;
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return other < one;
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator>(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return other < one;
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator>(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return other < one;
}
#endif
// operator >=
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value>
>
bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(one < other);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename Dummy = std::enable_if_t<
std::is_convertible<T, gsl::basic_string_span<std::add_const_t<CharT>, Extent>>::value
&& !gsl::details::is_basic_string_span<T>::value>
>
bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(one < other);
}
#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
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator>=(gsl::basic_string_span<CharT, Extent> one, const T& other) noexcept
{
return !(one < other);
}
template <typename CharT, std::ptrdiff_t Extent = gsl::dynamic_range, typename T,
typename DataType = typename T::value_type,
typename Dummy = std::enable_if_t<
!gsl::details::is_span<T>::value
&& !gsl::details::is_basic_string_span<T>::value
&& std::is_convertible<DataType*, CharT*>::value
&& std::is_same<std::decay_t<decltype(std::declval<T>().size(), *std::declval<T>().data())>, DataType>::value>
>
bool operator>=(const T& one, gsl::basic_string_span<CharT, Extent> other) noexcept
{
return !(one < other);
}
#endif
// VS 2013 workarounds
#ifdef _MSC_VER
#undef constexpr
#pragma pop_macro("constexpr")
#if _MSC_VER <= 1800
#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

View File

@ -1,183 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef GSL_STRING_VIEW_H
#define GSL_STRING_VIEW_H
#include "array_view.h"
#include <cstring>
namespace gsl
{
//
// czstring and wzstring
//
// 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
// type system for these types that will not either incur significant runtime costs or
// (sometimes needlessly) break existing programs when introduced.
//
template<size_t Max = dynamic_range>
using czstring = const char*;
template<size_t Max = dynamic_range>
using cwzstring = const wchar_t*;
template<size_t Max = dynamic_range>
using zstring = char*;
template<size_t Max = dynamic_range>
using wzstring = wchar_t*;
//
// string_view and relatives
//
// Note that Extent is always single-dimension only
// Note that SizeType is defaulted to be smaller than size_t which is the array_view default
//
// TODO (neilmac) once array_view regains configurable size_type, update these typedef's
//
template <class CharT, size_t Extent = dynamic_range>
using basic_string_view = array_view<CharT, Extent>;
template<size_t Extent = dynamic_range>
using string_view = basic_string_view<char, Extent>;
template<size_t Extent = dynamic_range>
using cstring_view = basic_string_view<const char, Extent>;
template<size_t Extent = dynamic_range>
using wstring_view = basic_string_view<wchar_t, Extent>;
template<size_t Extent = dynamic_range>
using cwstring_view = basic_string_view<const wchar_t, Extent>;
//
// ensure_sentinel()
//
// Provides a way to obtain an array_view 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<class T, class SizeType, const T Sentinel>
array_view<T, dynamic_range> ensure_sentinel(const T* seq, SizeType max = std::numeric_limits<SizeType>::max())
{
auto cur = seq;
while (SizeType(cur - seq) < max && *cur != Sentinel) ++cur;
fail_fast_assert(*cur == Sentinel);
return{ seq, SizeType(cur - seq) };
}
//
// ensure_z - creates a string_view for a czstring or cwzstring.
// Will fail fast if a null-terminator cannot be found before
// the limit of size_type.
//
template<class T>
inline basic_string_view<T, dynamic_range> ensure_z(T* const & sz, size_t max = std::numeric_limits<size_t>::max())
{
return ensure_sentinel<T, size_t, 0>(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_view<char, dynamic_range> ensure_z(char* const & sz, size_t max)
{
auto len = strnlen(sz, max);
fail_fast_assert(sz[len] == 0); return{ sz, len };
}
inline basic_string_view<const char, dynamic_range> ensure_z(const char* const& sz, size_t max)
{
auto len = strnlen(sz, max);
fail_fast_assert(sz[len] == 0); return{ sz, len };
}
inline basic_string_view<wchar_t, dynamic_range> ensure_z(wchar_t* const & sz, size_t max)
{
auto len = wcsnlen(sz, max);
fail_fast_assert(sz[len] == 0); return{ sz, len };
}
inline basic_string_view<const wchar_t, dynamic_range> ensure_z(const wchar_t* const & sz, size_t max)
{
auto len = wcsnlen(sz, max);
fail_fast_assert(sz[len] == 0); return{ sz, len };
}
template<class T, size_t N>
basic_string_view<T, dynamic_range> ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], N); }
template<class Cont>
basic_string_view<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_range> ensure_z(Cont& cont)
{
return ensure_z(cont.data(), cont.length());
}
//
// to_string() allow (explicit) conversions from string_view to string
//
template<class CharT, size_t Extent>
std::basic_string<typename std::remove_const<CharT>::type> to_string(basic_string_view<CharT, Extent> view)
{
return{ view.data(), view.length() };
}
template<class CharT, size_t Extent = dynamic_range>
class basic_zstring_builder
{
public:
using string_view_type = basic_string_view<CharT, Extent>;
using value_type = CharT;
using pointer = CharT*;
using size_type = typename string_view_type::size_type;
using iterator = typename string_view_type::iterator;
basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {}
template<size_t Size>
basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {}
pointer data() const { return sv_.data(); }
string_view_type view() const { return sv_; }
size_type length() const { return sv_.length(); }
pointer assume0() const { return data(); }
string_view_type ensure_z() const { return gsl::ensure_z(sv_); }
iterator begin() const { return sv_.begin(); }
iterator end() const { return sv_.end(); }
private:
string_view_type sv_;
};
template <size_t Max = dynamic_range>
using zstring_builder = basic_zstring_builder<char, Max>;
template <size_t Max = dynamic_range>
using wzstring_builder = basic_zstring_builder<wchar_t, Max>;
}
#endif // GSL_STRING_VIEW_H

View File

@ -9,19 +9,20 @@ include_directories(
./unittest-cpp ./unittest-cpp
) )
add_definitions(-DGSL_THROWS_FOR_TESTING) add_definitions(-DGSL_THROW_ON_CONTRACT_VIOLATION)
if(MSVC14 OR MSVC12) # has the support we need if(MSVC14 OR MSVC12) # has the support we need
# remove unnecessary warnings about unchecked iterators # remove unnecessary warnings about unchecked iterators
add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_compile_options(/W4)
else() else()
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
if(COMPILER_SUPPORTS_CXX14) if(COMPILER_SUPPORTS_CXX14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wno-missing-braces")
elseif(COMPILER_SUPPORTS_CXX11) elseif(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-missing-braces")
else() else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif() endif()
@ -32,7 +33,7 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp)
endif() endif()
function(add_gsl_test name) function(add_gsl_test name)
add_executable(${name} ${name}.cpp) add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/span.h ../include/string_span.h)
target_link_libraries(${name} UnitTest++) target_link_libraries(${name} UnitTest++)
install(TARGETS ${name} install(TARGETS ${name}
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
@ -43,8 +44,9 @@ function(add_gsl_test name)
) )
endfunction() endfunction()
add_gsl_test(array_view_tests) add_gsl_test(span_tests)
add_gsl_test(string_view_tests) add_gsl_test(strided_span_tests)
add_gsl_test(string_span_tests)
add_gsl_test(at_tests) add_gsl_test(at_tests)
add_gsl_test(bounds_tests) add_gsl_test(bounds_tests)
add_gsl_test(notnull_tests) add_gsl_test(notnull_tests)

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#include <UnitTest++/UnitTest++.h> #include <UnitTest++/UnitTest++.h>
#include <array_view.h> #include <span.h>
#include <vector> #include <vector>
using namespace std; using namespace std;
@ -23,16 +23,18 @@ using namespace gsl;;
namespace namespace
{ {
void use(unsigned int&) {} void use(std::ptrdiff_t&) {}
} }
SUITE(bounds_test) SUITE(bounds_test)
{ {
TEST(basic_bounds) TEST(basic_bounds)
{ {
for (auto point : static_bounds <unsigned int, dynamic_range, 3, 4 > { 2 }) for (auto point : static_bounds<dynamic_range, 3, 4 > { 2 })
{ {
for (unsigned int j = 0; j < decltype(point)::rank; j++) for (decltype(point)::size_type j = 0;
j < static_cast<decltype(point)::size_type>(decltype(point)::rank);
j++)
{ {
use(j); use(j);
use(point[j]); use(point[j]);
@ -42,24 +44,25 @@ SUITE(bounds_test)
TEST(bounds_basic) TEST(bounds_basic)
{ {
static_bounds<size_t, 3, 4, 5> b; static_bounds<3, 4, 5> b;
auto a = b.slice(); auto a = b.slice();
static_bounds<size_t, 4, dynamic_range, 2> x{ 4 }; (void)a;
static_bounds<4, dynamic_range, 2> x{ 4 };
x.slice().slice(); x.slice().slice();
} }
TEST (arrayview_iterator) TEST (arrayview_iterator)
{ {
static_bounds<size_t, 4, dynamic_range, 2> bounds{ 3 }; static_bounds<4, dynamic_range, 2> bounds{ 3 };
auto itr = bounds.begin(); auto itr = bounds.begin();
(void)itr;
#ifdef CONFIRM_COMPILATION_ERRORS #ifdef CONFIRM_COMPILATION_ERRORS
array_view< int, 4, dynamic_range, 2> av(nullptr, bounds); span<int, 4, dynamic_range, 2> av(nullptr, bounds);
auto itr2 = av.cbegin(); auto itr2 = av.cbegin();
for (auto & v : av) { for (auto& v : av) {
v = 4; v = 4;
} }
fill(av.begin(), av.end(), 0); fill(av.begin(), av.end(), 0);
@ -68,24 +71,25 @@ SUITE(bounds_test)
TEST (bounds_convertible) TEST (bounds_convertible)
{ {
static_bounds<size_t, 7, 4, 2> b1; static_bounds<7, 4, 2> b1;
static_bounds<size_t, 7, dynamic_range, 2> b2 = b1; static_bounds<7, dynamic_range, 2> b2 = b1;
(void)b2;
#ifdef CONFIRM_COMPILATION_ERRORS #ifdef CONFIRM_COMPILATION_ERRORS
static_bounds<size_t, 7, dynamic_range, 1> b4 = b2; static_bounds<7, dynamic_range, 1> b4 = b2;
#endif #endif
static_bounds<size_t, dynamic_range, dynamic_range, dynamic_range> b3 = b1; static_bounds<dynamic_range, dynamic_range, dynamic_range> b3 = b1;
static_bounds<int, 7, 4, 2> b4 = b3; static_bounds<7, 4, 2> b4 = b3;
(void)b4;
static_bounds<size_t, dynamic_range> b11; static_bounds<dynamic_range> b11;
static_bounds<size_t, dynamic_range> b5; static_bounds<dynamic_range> b5;
static_bounds<size_t, 34> b6; static_bounds<34> b6;
b5 = static_bounds<size_t, 20>(); b5 = static_bounds<20>();
CHECK_THROW(b6 = b5, fail_fast); CHECK_THROW(b6 = b5, fail_fast);
b5 = static_bounds<size_t, 34>(); b5 = static_bounds<34>();
b6 = b5; b6 = b5;
CHECK(b5 == b6); CHECK(b5 == b6);

View File

@ -69,7 +69,8 @@ SUITE(NotNullTests)
MyDerived derived; MyDerived derived;
Unrelated unrelated; Unrelated unrelated;
not_null<Unrelated*> u = &unrelated; not_null<Unrelated*> u = &unrelated;
not_null<MyDerived*> p = &derived; (void)u;
not_null<MyDerived*> p = &derived;
not_null<MyBase*> q = &base; not_null<MyBase*> q = &base;
q = p; // allowed with heterogeneous copy ctor q = p; // allowed with heterogeneous copy ctor
CHECK(q == p); CHECK(q == p);

1679
tests/span_tests.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,748 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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 <UnitTest++/UnitTest++.h>
#include <span.h>
#include <string>
#include <vector>
#include <list>
#include <iostream>
#include <memory>
#include <map>
using namespace std;
using namespace gsl;
namespace
{
struct BaseClass {};
struct DerivedClass : BaseClass {};
}
SUITE(strided_span_tests)
{
TEST (span_section_test)
{
int a[30][4][5];
auto av = as_span(a);
auto sub = av.section({15, 0, 0}, gsl::index<3>{2, 2, 2});
auto subsub = sub.section({1, 0, 0}, gsl::index<3>{1, 1, 1});
(void)subsub;
}
TEST(span_section)
{
std::vector<int> data(5 * 10);
std::iota(begin(data), end(data), 0);
const span<int, 5, 10> av = as_span(span<int>{data}, dim<5>(), dim<10>());
strided_span<int, 2> av_section_1 = av.section({ 1, 2 }, { 3, 4 });
CHECK((av_section_1[{0, 0}] == 12));
CHECK((av_section_1[{0, 1}] == 13));
CHECK((av_section_1[{1, 0}] == 22));
CHECK((av_section_1[{2, 3}] == 35));
strided_span<int, 2> av_section_2 = av_section_1.section({ 1, 2 }, { 2,2 });
CHECK((av_section_2[{0, 0}] == 24));
CHECK((av_section_2[{0, 1}] == 25));
CHECK((av_section_2[{1, 0}] == 34));
}
TEST(strided_span_constructors)
{
// Check stride constructor
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
const int carr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
strided_span<int, 1> sav1{ arr, {{9}, {1}} }; // T -> T
CHECK(sav1.bounds().index_bounds() == index<1>{ 9 });
CHECK(sav1.bounds().stride() == 1);
CHECK(sav1[0] == 1 && sav1[8] == 9);
strided_span<const int, 1> sav2{ carr, {{ 4 }, { 2 }} }; // const T -> const T
CHECK(sav2.bounds().index_bounds() == index<1>{ 4 });
CHECK(sav2.bounds().strides() == index<1>{2});
CHECK(sav2[0] == 1 && sav2[3] == 7);
strided_span<int, 2> sav3{ arr, {{ 2, 2 },{ 6, 2 }} }; // T -> const T
CHECK((sav3.bounds().index_bounds() == index<2>{ 2, 2 }));
CHECK((sav3.bounds().strides() == index<2>{ 6, 2 }));
CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7));
}
// Check span constructor
{
int arr[] = { 1, 2 };
// From non-cv-qualified source
{
const span<int> src = arr;
strided_span<int, 1> sav{ src, {2, 1} };
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav.bounds().strides() == index<1>{ 1 });
CHECK(sav[1] == 2);
#if _MSC_VER > 1800
//strided_span<const int, 1> sav_c{ {src}, {2, 1} };
strided_span<const int, 1> sav_c{ span<const int>{src}, strided_bounds<1>{2, 1} };
#else
strided_span<const int, 1> sav_c{ span<const int>{src}, strided_bounds<1>{2, 1} };
#endif
CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_c.bounds().strides() == index<1>{ 1 });
CHECK(sav_c[1] == 2);
#if _MSC_VER > 1800
strided_span<volatile int, 1> sav_v{ src, {2, 1} };
#else
strided_span<volatile int, 1> sav_v{ span<volatile int>{src}, strided_bounds<1>{2, 1} };
#endif
CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_v.bounds().strides() == index<1>{ 1 });
CHECK(sav_v[1] == 2);
#if _MSC_VER > 1800
strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
#else
strided_span<const volatile int, 1> sav_cv{ span<const volatile int>{src}, strided_bounds<1>{2, 1} };
#endif
CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
CHECK(sav_cv[1] == 2);
}
// From const-qualified source
{
const span<const int> src{ arr };
strided_span<const int, 1> sav_c{ src, {2, 1} };
CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_c.bounds().strides() == index<1>{ 1 });
CHECK(sav_c[1] == 2);
#if _MSC_VER > 1800
strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
#else
strided_span<const volatile int, 1> sav_cv{ span<const volatile int>{src}, strided_bounds<1>{2, 1} };
#endif
CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
CHECK(sav_cv[1] == 2);
}
// From volatile-qualified source
{
const span<volatile int> src{ arr };
strided_span<volatile int, 1> sav_v{ src, {2, 1} };
CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_v.bounds().strides() == index<1>{ 1 });
CHECK(sav_v[1] == 2);
#if _MSC_VER > 1800
strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
#else
strided_span<const volatile int, 1> sav_cv{ span<const volatile int>{src}, strided_bounds<1>{2, 1} };
#endif
CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
CHECK(sav_cv[1] == 2);
}
// From cv-qualified source
{
const span<const volatile int> src{ arr };
strided_span<const volatile int, 1> sav_cv{ src, {2, 1} };
CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav_cv.bounds().strides() == index<1>{ 1 });
CHECK(sav_cv[1] == 2);
}
}
// Check const-casting constructor
{
int arr[2] = { 4, 5 };
const span<int, 2> av(arr, 2);
span<const int, 2> av2{ av };
CHECK(av2[1] == 5);
static_assert(std::is_convertible<const span<int, 2>, span<const int, 2>>::value, "ctor is not implicit!");
const strided_span<int, 1> src{ arr, {2, 1} };
strided_span<const int, 1> sav{ src };
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav.bounds().stride() == 1);
CHECK(sav[1] == 5);
static_assert(std::is_convertible<const strided_span<int, 1>, strided_span<const int, 1>>::value, "ctor is not implicit!");
}
// Check copy constructor
{
int arr1[2] = { 3, 4 };
const strided_span<int, 1> src1{ arr1, {2, 1} };
strided_span<int, 1> sav1{ src1 };
CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav1.bounds().stride() == 1);
CHECK(sav1[0] == 3);
int arr2[6] = { 1, 2, 3, 4, 5, 6 };
const strided_span<const int, 2> src2{ arr2, {{ 3, 2 }, { 2, 1 }} };
strided_span<const int, 2> sav2{ src2 };
CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
}
// Check const-casting assignment operator
{
int arr1[2] = { 1, 2 };
int arr2[6] = { 3, 4, 5, 6, 7, 8 };
const strided_span<int, 1> src{ arr1, {{2}, {1}} };
strided_span<const int, 1> sav{ arr2, {{3}, {2}} };
strided_span<const int, 1>& sav_ref = (sav = src);
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav.bounds().strides() == index<1>{ 1 });
CHECK(sav[0] == 1);
CHECK(&sav_ref == &sav);
}
// Check copy assignment operator
{
int arr1[2] = { 3, 4 };
int arr1b[1] = { 0 };
const strided_span<int, 1> src1{ arr1, {2, 1} };
strided_span<int, 1> sav1{ arr1b, {1, 1} };
strided_span<int, 1>& sav1_ref = (sav1 = src1);
CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav1.bounds().strides() == index<1>{ 1 });
CHECK(sav1[0] == 3);
CHECK(&sav1_ref == &sav1);
const int arr2[6] = { 1, 2, 3, 4, 5, 6 };
const int arr2b[1] = { 0 };
const strided_span<const int, 2> src2{ arr2, {{ 3, 2 },{ 2, 1 }} };
strided_span<const int, 2> sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} };
strided_span<const int, 2>& sav2_ref = (sav2 = src2);
CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
CHECK(&sav2_ref == &sav2);
}
}
TEST(strided_span_slice)
{
std::vector<int> data(5 * 10);
std::iota(begin(data), end(data), 0);
const span<int, 5, 10> src = as_span(span<int>{data}, dim<5>(), dim<10>());
const strided_span<int, 2> sav{ src, {{5, 10}, {10, 1}} };
#ifdef CONFIRM_COMPILATION_ERRORS
const strided_span<const int, 2> csav{ {src},{ { 5, 10 },{ 10, 1 } } };
#endif
const strided_span<const int, 2> csav{ span<const int, 5, 10>{ src }, { { 5, 10 },{ 10, 1 } } };
strided_span<int, 1> sav_sl = sav[2];
CHECK(sav_sl[0] == 20);
CHECK(sav_sl[9] == 29);
strided_span<const int, 1> csav_sl = sav[3];
CHECK(csav_sl[0] == 30);
CHECK(csav_sl[9] == 39);
CHECK(sav[4][0] == 40);
CHECK(sav[4][9] == 49);
}
TEST(strided_span_column_major)
{
// strided_span may be used to accomodate more peculiar
// use cases, such as column-major multidimensional array
// (aka. "FORTRAN" layout).
int cm_array[3 * 5] = {
1, 4, 7, 10, 13,
2, 5, 8, 11, 14,
3, 6, 9, 12, 15
};
strided_span<int, 2> cm_sav{ cm_array, {{ 5, 3 },{ 1, 5 }} };
// Accessing elements
CHECK((cm_sav[{0, 0}] == 1));
CHECK((cm_sav[{0, 1}] == 2));
CHECK((cm_sav[{1, 0}] == 4));
CHECK((cm_sav[{4, 2}] == 15));
// Slice
strided_span<int, 1> cm_sl = cm_sav[3];
CHECK(cm_sl[0] == 10);
CHECK(cm_sl[1] == 11);
CHECK(cm_sl[2] == 12);
// Section
strided_span<int, 2> cm_sec = cm_sav.section( { 2, 1 }, { 3, 2 });
CHECK((cm_sec.bounds().index_bounds() == index<2>{3, 2}));
CHECK((cm_sec[{0, 0}] == 8));
CHECK((cm_sec[{0, 1}] == 9));
CHECK((cm_sec[{1, 0}] == 11));
CHECK((cm_sec[{2, 1}] == 15));
}
TEST(strided_span_bounds)
{
int arr[] = { 0, 1, 2, 3 };
span<int> av(arr);
{
// incorrect sections
CHECK_THROW(av.section(0, 0)[0], fail_fast);
CHECK_THROW(av.section(1, 0)[0], fail_fast);
CHECK_THROW(av.section(1, 1)[1], fail_fast);
CHECK_THROW(av.section(2, 5), fail_fast);
CHECK_THROW(av.section(5, 2), fail_fast);
CHECK_THROW(av.section(5, 0), fail_fast);
CHECK_THROW(av.section(0, 5), fail_fast);
CHECK_THROW(av.section(5, 5), fail_fast);
}
{
// zero stride
strided_span<int, 1> sav{ av,{ { 4 },{} } };
CHECK(sav[0] == 0);
CHECK(sav[3] == 0);
CHECK_THROW(sav[4], fail_fast);
}
{
// zero extent
strided_span<int, 1> sav{ av,{ {},{ 1 } } };
CHECK_THROW(sav[0], fail_fast);
}
{
// zero extent and stride
strided_span<int, 1> sav{ av,{ {},{} } };
CHECK_THROW(sav[0], fail_fast);
}
{
// strided array ctor with matching strided bounds
strided_span<int, 1> sav{ arr,{ 4, 1 } };
CHECK(sav.bounds().index_bounds() == index<1>{ 4 });
CHECK(sav[3] == 3);
CHECK_THROW(sav[4], fail_fast);
}
{
// strided array ctor with smaller strided bounds
strided_span<int, 1> sav{ arr,{ 2, 1 } };
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav[1] == 1);
CHECK_THROW(sav[2], fail_fast);
}
{
// strided array ctor with fitting irregular bounds
strided_span<int, 1> sav{ arr,{ 2, 3 } };
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav[0] == 0);
CHECK(sav[1] == 3);
CHECK_THROW(sav[2], fail_fast);
}
{
// bounds cross data boundaries - from static arrays
CHECK_THROW((strided_span<int, 1> { arr, { 3, 2 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { arr, { 3, 3 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { arr, { 4, 5 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { arr, { 5, 1 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { arr, { 5, 5 } }), fail_fast);
}
{
// bounds cross data boundaries - from array view
CHECK_THROW((strided_span<int, 1> { av, { 3, 2 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av, { 3, 3 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av, { 4, 5 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av, { 5, 1 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av, { 5, 5 } }), fail_fast);
}
{
// bounds cross data boundaries - from dynamic arrays
CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 3, 2 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 3, 3 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 4, 5 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 5, 1 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av.data(), 4, { 5, 5 } }), fail_fast);
CHECK_THROW((strided_span<int, 1> { av.data(), 2, { 2, 2 } }), fail_fast);
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
strided_span<int, 1> sav0{ av.data(), { 3, 2 } };
strided_span<int, 1> sav1{ arr, { 1 } };
strided_span<int, 1> sav2{ arr, { 1,1,1 } };
strided_span<int, 1> sav3{ av, { 1 } };
strided_span<int, 1> sav4{ av, { 1,1,1 } };
strided_span<int, 2> sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } };
strided_span<int, 2> sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } };
strided_span<int, 2> sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } };
index<1> index{ 0, 1 };
strided_span<int, 1> sav8{ arr,{ 1,{ 1,1 } } };
strided_span<int, 1> sav9{ arr,{ { 1,1 },{ 1,1 } } };
strided_span<int, 1> sav10{ av,{ 1,{ 1,1 } } };
strided_span<int, 1> sav11{ av,{ { 1,1 },{ 1,1 } } };
strided_span<int, 2> sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } };
strided_span<int, 2> sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } };
strided_span<int, 2> sav14{ av.as_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } };
}
#endif
}
TEST(strided_span_type_conversion)
{
int arr[] = { 0, 1, 2, 3 };
span<int> av(arr);
{
strided_span<int, 1> sav{ av.data(), av.size(), { av.size() / 2, 2 } };
#ifdef CONFIRM_COMPILATION_ERRORS
strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
#endif
}
{
strided_span<int, 1> sav{ av, { av.size() / 2, 2 } };
#ifdef CONFIRM_COMPILATION_ERRORS
strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
#endif
}
span<const byte, dynamic_range> bytes = as_bytes(av);
// retype strided array with regular strides - from raw data
{
strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } };
strided_span<const byte, 2> sav2{ bytes.data(), bytes.size(), bounds };
strided_span<const int, 2> sav3 = sav2.as_strided_span<const int>();
CHECK(sav3[0][0] == 0);
CHECK(sav3[1][0] == 2);
CHECK_THROW(sav3[1][1], fail_fast);
CHECK_THROW(sav3[0][1], fail_fast);
}
// retype strided array with regular strides - from span
{
strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } };
span<const byte, 2, dynamic_range> bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2));
strided_span<const byte, 2> sav2{ bytes2, bounds };
strided_span<int, 2> sav3 = sav2.as_strided_span<int>();
CHECK(sav3[0][0] == 0);
CHECK(sav3[1][0] == 2);
CHECK_THROW(sav3[1][1], fail_fast);
CHECK_THROW(sav3[0][1], fail_fast);
}
// retype strided array with not enough elements - last dimension of the array is too small
{
strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } };
span<const byte, 2, dynamic_range> bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2));
strided_span<const byte, 2> sav2{ bytes2, bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
// retype strided array with not enough elements - strides are too small
{
strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } };
span<const byte, 2, dynamic_range> bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2));
strided_span<const byte, 2> sav2{ bytes2, bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
// retype strided array with not enough elements - last dimension does not divide by the new typesize
{
strided_bounds<2> bounds{ { 2,6 },{ 4, 1 } };
span<const byte, 2, dynamic_range> bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2));
strided_span<const byte, 2> sav2{ bytes2, bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
// retype strided array with not enough elements - strides does not divide by the new typesize
{
strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } };
span<const byte, 2, dynamic_range> bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2));
strided_span<const byte, 2> sav2{ bytes2, bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
// retype strided array with irregular strides - from raw data
{
strided_bounds<1> bounds{ bytes.size() / 2, 2 };
strided_span<const byte, 1> sav2{ bytes.data(), bytes.size(), bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
// retype strided array with irregular strides - from span
{
strided_bounds<1> bounds{ bytes.size() / 2, 2 };
strided_span<const byte, 1> sav2{ bytes, bounds };
CHECK_THROW(sav2.as_strided_span<int>(), fail_fast);
}
}
TEST(empty_strided_spans)
{
{
span<int, 0> empty_av(nullptr);
strided_span<int, 1> empty_sav{ empty_av, { 0, 1 } };
CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 });
CHECK_THROW(empty_sav[0], fail_fast);
CHECK_THROW(empty_sav.begin()[0], fail_fast);
CHECK_THROW(empty_sav.cbegin()[0], fail_fast);
for (auto& v : empty_sav)
{
(void)v;
CHECK(false);
}
}
{
strided_span<int, 1> empty_sav{ nullptr, 0, { 0, 1 } };
CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 });
CHECK_THROW(empty_sav[0], fail_fast);
CHECK_THROW(empty_sav.begin()[0], fail_fast);
CHECK_THROW(empty_sav.cbegin()[0], fail_fast);
for (auto& v : empty_sav)
{
(void)v;
CHECK(false);
}
}
}
void iterate_every_other_element(span<int, dynamic_range> av)
{
// pick every other element
auto length = av.size() / 2;
#if _MSC_VER > 1800
auto bounds = strided_bounds<1>({length}, {2});
#else
auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 });
#endif
strided_span<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
CHECK(strided.size() == length);
CHECK(strided.bounds().index_bounds()[0] == length);
for (auto i = 0; i < strided.size(); ++i)
{
CHECK(strided[i] == av[2 * i + 1]);
}
int idx = 0;
for (auto num : strided)
{
CHECK(num == av[2 * idx + 1]);
idx++;
}
}
TEST(strided_span_section_iteration)
{
int arr[8] = {4,0,5,1,6,2,7,3};
// static bounds
{
span<int, 8> av(arr, 8);
iterate_every_other_element(av);
}
// dynamic bounds
{
span<int, dynamic_range> av(arr, 8);
iterate_every_other_element(av);
}
}
TEST(dynamic_strided_span_section_iteration)
{
auto arr = new int[8];
for (int i = 0; i < 4; ++i)
{
arr[2 * i] = 4 + i;
arr[2 * i + 1] = i;
}
auto av = as_span(arr, 8);
iterate_every_other_element(av);
delete[] arr;
}
void iterate_second_slice(span<int, dynamic_range, dynamic_range, dynamic_range> av)
{
int expected[6] = {2,3,10,11,18,19};
auto section = av.section({0,1,0}, {3,1,2});
for (auto i = 0; i < section.extent<0>(); ++i)
{
for (auto j = 0; j < section.extent<1>(); ++j)
for (auto k = 0; k < section.extent<2>(); ++k)
{
auto idx = index<3>{i,j,k}; // avoid braces in the CHECK macro
CHECK(section[idx] == expected[2 * i + 2 * j + k]);
}
}
for (auto i = 0; i < section.extent<0>(); ++i)
{
for (auto j = 0; j < section.extent<1>(); ++j)
for (auto k = 0; k < section.extent<2>(); ++k)
CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]);
}
int i = 0;
for (auto num : section)
{
CHECK(num == expected[i]);
i++;
}
}
TEST(strided_span_section_iteration_3d)
{
int arr[3][4][2];
for (auto i = 0; i < 3; ++i)
{
for (auto j = 0; j < 4; ++j)
for (auto k = 0; k < 2; ++k)
arr[i][j][k] = 8 * i + 2 * j + k;
}
{
span<int, 3, 4, 2> av = arr;
iterate_second_slice(av);
}
}
TEST(dynamic_strided_span_section_iteration_3d)
{
auto height = 12, width = 2;
auto size = height * width;
auto arr = new int[size];
for (auto i = 0; i < size; ++i)
{
arr[i] = i;
}
{
auto av = as_span(as_span(arr, 24), dim<3>(), dim<4>(), dim<2>());
iterate_second_slice(av);
}
{
auto av = as_span(as_span(arr, 24), dim<>(3), dim<4>(), dim<2>());
iterate_second_slice(av);
}
{
auto av = as_span(as_span(arr, 24), dim<3>(), dim<>(4), dim<2>());
iterate_second_slice(av);
}
{
auto av = as_span(as_span(arr, 24), dim<3>(), dim<4>(), dim<>(2));
iterate_second_slice(av);
}
delete[] arr;
}
TEST(strided_span_conversion)
{
// get an span of 'c' values from the list of X's
struct X { int a; int b; int c; };
X arr[4] = {{0,1,2},{3,4,5},{6,7,8},{9,10,11}};
int s = sizeof(int) / sizeof(byte);
auto d2 = 3 * s;
auto d1 = sizeof(int) * 12 / d2;
// convert to 4x12 array of bytes
auto av = as_span(as_bytes(as_span(arr, 4)), dim<>(d1), dim<>(d2));
CHECK(av.bounds().index_bounds()[0] == 4);
CHECK(av.bounds().index_bounds()[1] == 12);
// get the last 4 columns
auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2], arr[0].c[3] } , { arr[1].c[0], ... } , ... }
// convert to array 4x1 array of integers
auto cs = section.as_strided_span<int>(); // { { arr[0].c }, {arr[1].c } , ... }
CHECK(cs.bounds().index_bounds()[0] == 4);
CHECK(cs.bounds().index_bounds()[1] == 1);
// transpose to 1x4 array
strided_bounds<2> reverse_bounds{
{cs.bounds().index_bounds()[1] , cs.bounds().index_bounds()[0]},
{cs.bounds().strides()[1], cs.bounds().strides()[0]}
};
strided_span<int, 2> transposed{cs.data(), cs.bounds().total_size(), reverse_bounds};
// slice to get a one-dimensional array of c's
strided_span<int, 1> result = transposed[0];
CHECK(result.bounds().index_bounds()[0] == 4);
CHECK_THROW(result.bounds().index_bounds()[1], fail_fast);
int i = 0;
for (auto& num : result)
{
CHECK(num == arr[i].c);
i++;
}
}
}
int main(int, const char *[])
{
return UnitTest::RunAllTests();
}

808
tests/string_span_tests.cpp Normal file
View File

@ -0,0 +1,808 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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 <UnitTest++/UnitTest++.h>
#include <cstdlib>
#include <string_span.h>
#include <vector>
using namespace std;
using namespace gsl;
SUITE(string_span_tests)
{
TEST(TestLiteralConstruction)
{
cwstring_span<> v = ensure_z(L"Hello");
CHECK(5 == v.length());
#ifdef CONFIRM_COMPILATION_ERRORS
wstring_span<> v2 = ensure0(L"Hello");
#endif
}
TEST(TestConstructFromStdString)
{
std::string s = "Hello there world";
cstring_span<> v = s;
CHECK(v.length() == static_cast<cstring_span<>::size_type>(s.length()));
}
TEST(TestConstructFromStdVector)
{
std::vector<char> vec(5, 'h');
string_span<> v = vec;
CHECK(v.length() == static_cast<string_span<>::size_type>(vec.size()));
}
TEST(TestStackArrayConstruction)
{
wchar_t stack_string[] = L"Hello";
{
cwstring_span<> v = ensure_z(stack_string);
CHECK(v.length() == 5);
}
{
cwstring_span<> v = stack_string;
CHECK(v.length() == 5);
}
{
wstring_span<> v = ensure_z(stack_string);
CHECK(v.length() == 5);
}
{
wstring_span<> v = stack_string;
CHECK(v.length() == 5);
}
}
TEST(TestConstructFromConstCharPointer)
{
const char* s = "Hello";
cstring_span<> v = ensure_z(s);
CHECK(v.length() == 5);
}
TEST(TestConversionToConst)
{
char stack_string[] = "Hello";
string_span<> v = ensure_z(stack_string);
cstring_span<> v2 = v;
CHECK(v.length() == v2.length());
}
TEST(TestConversionFromConst)
{
char stack_string[] = "Hello";
cstring_span<> v = ensure_z(stack_string);
(void)v;
#ifdef CONFIRM_COMPILATION_ERRORS
string_span<> v2 = v;
string_span<> v3 = "Hello";
#endif
}
TEST(TestToString)
{
auto s = gsl::to_string(cstring_span<>{});
CHECK(s.length() == 0);
char stack_string[] = "Hello";
cstring_span<> v = ensure_z(stack_string);
auto s2 = gsl::to_string(v);
CHECK(static_cast<cstring_span<>::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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
gsl::span<const char> sp = ensure_z("Hello");
// 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 to span
CHECK(span == cstring_span<>(sp));
// comparison to string_span
CHECK(span == span);
// 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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
gsl::span<char> sp = ensure_z(ar1);
// 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));
// comparison to span
CHECK(span == string_span<>(sp));
// comparison to string_span
CHECK(span == span);
}
{
const char ar[] = { 'H', 'e', 'l', 'l', 'o' };
const char ar1[] = "Hello";
const char ar2[10] = "Hello";
const std::string str = "Hello";
const std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
gsl::span<const char> sp = ensure_z("Hello");
cstring_span<> span = "Hello";
// const span, const other type
CHECK(span == "Hello");
CHECK(span == ar);
CHECK(span == ar1);
CHECK(span == ar2);
#ifdef CONFIRM_COMPILATION_ERRORS
const char* ptr = "Hello";
CHECK(span == ptr);
#endif
CHECK(span == str);
CHECK(span == vec);
CHECK(span == sp);
CHECK("Hello" == span);
CHECK(ar == span);
CHECK(ar1 == span);
CHECK(ar2 == span);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(ptr == span);
#endif
CHECK(str == span);
CHECK(vec == span);
CHECK(sp == span);
// const span, non-const other type
char _ar[] = { 'H', 'e', 'l', 'l', 'o' };
char _ar1[] = "Hello";
char _ar2[10] = "Hello";
char* _ptr = _ar;
std::string _str = "Hello";
std::vector<char> _vec = { 'H', 'e', 'l', 'l', 'o' };
gsl::span<char> _sp{ _ar, 5 };
CHECK(span == _ar);
CHECK(span == _ar1);
CHECK(span == _ar2);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(span == _ptr);
#endif
CHECK(span == _str);
CHECK(span == _vec);
CHECK(span == _sp);
CHECK(_ar == span);
CHECK(_ar1 == span);
CHECK(_ar2 == span);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(_ptr == span);
#endif
CHECK(_str == span);
CHECK(_vec == span);
CHECK(_sp == span);
string_span<> _span{ _ptr, 5 };
// non-const span, non-const other type
CHECK(_span == _ar);
CHECK(_span == _ar1);
CHECK(_span == _ar2);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(_span == _ptr);
#endif
CHECK(_span == _str);
CHECK(_span == _vec);
CHECK(_span == _sp);
CHECK(_ar == _span);
CHECK(_ar1 == _span);
CHECK(_ar2 == _span);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(_ptr == _span);
#endif
CHECK(_str == _span);
CHECK(_vec == _span);
CHECK(_sp == _span);
// non-const span, const other type
CHECK(_span == "Hello");
CHECK(_span == ar);
CHECK(_span == ar1);
CHECK(_span == ar2);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(_span == ptr);
#endif
CHECK(_span == str);
CHECK(_span == vec);
CHECK(_span == sp);
CHECK("Hello" == _span);
CHECK(ar == _span);
CHECK(ar1 == _span);
CHECK(ar2 == _span);
#ifdef CONFIRM_COMPILATION_ERRORS
CHECK(ptr == _span);
#endif
CHECK(str == _span);
CHECK(vec == _span);
CHECK(sp == _span);
// two spans
CHECK(_span == span);
CHECK(span == _span);
}
{
std::vector<char> str1 = { 'H', 'e', 'l', 'l', 'o' };
cstring_span<> span1 = str1;
std::vector<char> 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<char> 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<char> 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(ConstrutorsEnsureZ)
{
// 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
// default
{
cstring_span<> span;
CHECK(span.length() == 0);
}
// from nullptr
{
cstring_span<> span(nullptr);
CHECK(span.length() == 0);
}
// 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 const ptr and length, include 0
{
const char* ptr = "Hello";
cstring_span<> span{ ptr, 6 };
CHECK(span.length() == 6);
}
// from const ptr and length, 0 inside
{
const char* ptr = "He\0lo";
cstring_span<> span{ ptr, 5 };
CHECK(span.length() == 5);
}
// from non-const ptr and length
{
char ar[] = { 'H', 'e', 'l', 'l', 'o' };
char* ptr = ar;
cstring_span<> span{ ptr, 5 };
CHECK(span.length() == 5);
}
// from non-const ptr and length, 0 inside
{
char ar[] = { 'H', 'e', '\0', 'l', 'o' };
char* ptr = ar;
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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
cstring_span<> span = vec;
CHECK(span.length() == 5);
}
// from non-const vector
{
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
cstring_span<> span = vec;
CHECK(span.length() == 5);
}
// from const span
{
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
const span<const char> inner = vec;
cstring_span<> span = inner;
CHECK(span.length() == 5);
}
// from non-const span
{
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
span<char> inner = vec;
cstring_span<> span = inner;
CHECK(span.length() == 5);
}
// from const string_span
{
std::vector<char> 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<char> 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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
string_span<> span = vec;
CHECK(span.length() == 5);
#endif
}
// from non-const vector
{
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
string_span<> span = vec;
CHECK(span.length() == 5);
}
// from const span
{
#ifdef CONFIRM_COMPILATION_ERRORS
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
const span<const char> inner = vec;
string_span<> span = inner;
CHECK(span.length() == 5);
#endif
}
// from non-const span
{
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
span<char> 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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
const span<char> inner = vec;
string_span<> span = inner;
CHECK(span.length() == 5);
#endif
}
// from const string_span
{
#ifdef CONFIRM_COMPILATION_ERRORS
std::vector<char> 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<char> 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<char> 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<char> vec = { 'H', 'e', 'l', 'l', 'o' };
const string_span<> tmp = vec;
string_span<> span = tmp;
CHECK(span.length() == 5);
}
}
template<typename T>
T move_wrapper(T&& t)
{
return std::move(t);
}
template <class T>
T create() { return T{}; }
template <class T>
void use(basic_string_span<T, gsl::dynamic_range> 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<const char> span = ensure_z("Hello");
cstring_span<> span1 = std::move(span);
CHECK(span1.length() == 5);
}
{
span<const char> 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::string>(std::move(str));
CHECK(span.length() == 5);
#endif
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
use<char>(create<string>());
#endif
}
// move container
{
#ifdef CONFIRM_COMPILATION_ERRORS
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
string_span<> span = std::move(vec);
CHECK(span.length() == 5);
#endif
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
std::vector<char> vec = { 'H', 'e', 'l', 'l', 'o' };
string_span<> span = move_wrapper<std::vector<char>>(std::move(vec));
CHECK(span.length() == 5);
#endif
}
{
#ifdef CONFIRM_COMPILATION_ERRORS
use<char>(create<std::vector<char>>());
#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 *[])
{
return UnitTest::RunAllTests();
}

View File

@ -1,112 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// 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 <UnitTest++/UnitTest++.h>
#include <string_view.h>
#include <vector>
#include <cstdlib>
using namespace std;
using namespace gsl;
SUITE(string_view_tests)
{
TEST(TestLiteralConstruction)
{
cwstring_view<> v = ensure_z(L"Hello");
CHECK(5 == v.length());
#ifdef CONFIRM_COMPILATION_ERRORS
wstring_view<> v2 = ensure0(L"Hello");
#endif
}
TEST(TestConstructFromStdString)
{
std::string s = "Hello there world";
cstring_view<> v = s;
CHECK(v.length() == s.length());
}
TEST(TestConstructFromStdVector)
{
std::vector<char> vec(5, 'h');
string_view<> v = vec;
CHECK(v.length() == vec.size());
}
TEST(TestStackArrayConstruction)
{
wchar_t stack_string[] = L"Hello";
{
cwstring_view<> v = ensure_z(stack_string);
CHECK(v.length() == 5);
CHECK(v.used_length() == v.length());
}
{
cwstring_view<> v = stack_string;
CHECK(v.length() == 6);
CHECK(v.used_length() == v.length());
}
{
wstring_view<> v = ensure_z(stack_string);
CHECK(v.length() == 5);
CHECK(v.used_length() == v.length());
}
{
wstring_view<> v = stack_string;
CHECK(v.length() == 6);
CHECK(v.used_length() == v.length());
}
}
TEST(TestConstructFromConstCharPointer)
{
const char* s = "Hello";
cstring_view<> v = ensure_z(s);
CHECK(v.length() == 5);
CHECK(v.used_length() == v.length());
}
TEST(TestConversionToConst)
{
char stack_string[] = "Hello";
string_view<> v = ensure_z(stack_string);
cstring_view<> v2 = v;
CHECK(v.length() == v2.length());
}
TEST(TestConversionFromConst)
{
char stack_string[] = "Hello";
cstring_view<> v = ensure_z(stack_string);
#ifdef CONFIRM_COMPILATION_ERRORS
string_view<> v2 = v;
string_view<> v3 = "Hello";
#endif
}
}
int main(int, const char *[])
{
return UnitTest::RunAllTests();
}