mirror of
https://github.com/microsoft/GSL.git
synced 2024-11-03 17:56:43 -05:00
commit
004f0aba3b
@ -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).
|
||||
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.
|
||||
|
||||
> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to
|
||||
|
1998
include/array_view.h
1998
include/array_view.h
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -19,23 +19,23 @@
|
||||
#ifndef GSL_GSL_H
|
||||
#define GSL_GSL_H
|
||||
|
||||
#include "array_view.h" // array_view, strided_array_view...
|
||||
#include "string_view.h" // zstring, string_view, zstring_builder...
|
||||
#include "gsl_assert.h" // Ensures/Expects
|
||||
#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>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
// No MSVC does constexpr fully yet
|
||||
#pragma push_macro("constexpr")
|
||||
#define constexpr /* nothing */
|
||||
#define constexpr
|
||||
|
||||
// MSVC 2013 workarounds
|
||||
#if _MSC_VER <= 1800
|
||||
|
||||
// noexcept is not understood
|
||||
#ifndef GSL_THROWS_FOR_TESTING
|
||||
#define noexcept /* nothing */
|
||||
#endif
|
||||
#pragma push_macro("noexcept")
|
||||
#define noexcept
|
||||
|
||||
// turn off some misguided warnings
|
||||
#pragma warning(push)
|
||||
@ -45,11 +45,6 @@
|
||||
|
||||
#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
|
||||
{
|
||||
@ -63,62 +58,6 @@ using std::shared_ptr;
|
||||
template <class 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
|
||||
@ -181,7 +120,7 @@ private:
|
||||
|
||||
// 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
|
||||
void ensure_invariant() const { fail_fast_assert(ptr_ != nullptr); }
|
||||
void ensure_invariant() const { Expects(ptr_ != nullptr); }
|
||||
|
||||
// unwanted operators...pointers only point to single objects!
|
||||
// TODO ensure all arithmetic ops on this type are unavailable
|
||||
@ -216,18 +155,14 @@ namespace std
|
||||
#pragma pop_macro("constexpr")
|
||||
|
||||
#if _MSC_VER <= 1800
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifndef GSL_THROWS_FOR_TESTING
|
||||
#pragma undef noexcept
|
||||
#endif // GSL_THROWS_FOR_TESTING
|
||||
#undef noexcept
|
||||
#pragma pop_macro("noexcept")
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif // _MSC_VER <= 1800
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if defined(GSL_THROWS_FOR_TESTING)
|
||||
#undef noexcept
|
||||
#endif // GSL_THROWS_FOR_TESTING
|
||||
|
||||
#endif // GSL_GSL_H
|
||||
|
78
include/gsl_assert.h
Normal file
78
include/gsl_assert.h
Normal 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
132
include/gsl_util.h
Normal 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
2224
include/span.h
Normal file
File diff suppressed because it is too large
Load Diff
881
include/string_span.h
Normal file
881
include/string_span.h
Normal 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
|
@ -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
|
@ -9,19 +9,20 @@ include_directories(
|
||||
./unittest-cpp
|
||||
)
|
||||
|
||||
add_definitions(-DGSL_THROWS_FOR_TESTING)
|
||||
add_definitions(-DGSL_THROW_ON_CONTRACT_VIOLATION)
|
||||
|
||||
if(MSVC14 OR MSVC12) # has the support we need
|
||||
# remove unnecessary warnings about unchecked iterators
|
||||
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
|
||||
add_compile_options(/W4)
|
||||
else()
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
|
||||
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)
|
||||
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()
|
||||
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
|
||||
endif()
|
||||
@ -32,7 +33,7 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp)
|
||||
endif()
|
||||
|
||||
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++)
|
||||
install(TARGETS ${name}
|
||||
RUNTIME DESTINATION bin
|
||||
@ -43,8 +44,9 @@ function(add_gsl_test name)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
add_gsl_test(array_view_tests)
|
||||
add_gsl_test(string_view_tests)
|
||||
add_gsl_test(span_tests)
|
||||
add_gsl_test(strided_span_tests)
|
||||
add_gsl_test(string_span_tests)
|
||||
add_gsl_test(at_tests)
|
||||
add_gsl_test(bounds_tests)
|
||||
add_gsl_test(notnull_tests)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <UnitTest++/UnitTest++.h>
|
||||
#include <array_view.h>
|
||||
#include <span.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
@ -23,16 +23,18 @@ using namespace gsl;;
|
||||
|
||||
namespace
|
||||
{
|
||||
void use(unsigned int&) {}
|
||||
void use(std::ptrdiff_t&) {}
|
||||
}
|
||||
|
||||
SUITE(bounds_test)
|
||||
{
|
||||
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(point[j]);
|
||||
@ -42,24 +44,25 @@ SUITE(bounds_test)
|
||||
|
||||
TEST(bounds_basic)
|
||||
{
|
||||
static_bounds<size_t, 3, 4, 5> b;
|
||||
static_bounds<3, 4, 5> b;
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
(void)itr;
|
||||
#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();
|
||||
|
||||
for (auto & v : av) {
|
||||
for (auto& v : av) {
|
||||
v = 4;
|
||||
}
|
||||
fill(av.begin(), av.end(), 0);
|
||||
@ -68,24 +71,25 @@ SUITE(bounds_test)
|
||||
|
||||
TEST (bounds_convertible)
|
||||
{
|
||||
static_bounds<size_t, 7, 4, 2> b1;
|
||||
static_bounds<size_t, 7, dynamic_range, 2> b2 = b1;
|
||||
|
||||
static_bounds<7, 4, 2> b1;
|
||||
static_bounds<7, dynamic_range, 2> b2 = b1;
|
||||
(void)b2;
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
static_bounds<size_t, 7, dynamic_range, 1> b4 = b2;
|
||||
static_bounds<7, dynamic_range, 1> b4 = b2;
|
||||
#endif
|
||||
|
||||
static_bounds<size_t, dynamic_range, dynamic_range, dynamic_range> b3 = b1;
|
||||
static_bounds<int, 7, 4, 2> b4 = b3;
|
||||
static_bounds<dynamic_range, dynamic_range, dynamic_range> b3 = b1;
|
||||
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<size_t, 34> b6;
|
||||
static_bounds<dynamic_range> b5;
|
||||
static_bounds<34> b6;
|
||||
|
||||
b5 = static_bounds<size_t, 20>();
|
||||
b5 = static_bounds<20>();
|
||||
CHECK_THROW(b6 = b5, fail_fast);
|
||||
b5 = static_bounds<size_t, 34>();
|
||||
b5 = static_bounds<34>();
|
||||
b6 = b5;
|
||||
|
||||
CHECK(b5 == b6);
|
||||
|
@ -69,6 +69,7 @@ SUITE(NotNullTests)
|
||||
MyDerived derived;
|
||||
Unrelated unrelated;
|
||||
not_null<Unrelated*> u = &unrelated;
|
||||
(void)u;
|
||||
not_null<MyDerived*> p = &derived;
|
||||
not_null<MyBase*> q = &base;
|
||||
q = p; // allowed with heterogeneous copy ctor
|
||||
|
1679
tests/span_tests.cpp
Normal file
1679
tests/span_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
748
tests/strided_span_tests.cpp
Normal file
748
tests/strided_span_tests.cpp
Normal 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
808
tests/string_span_tests.cpp
Normal 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();
|
||||
}
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user