From 836832914241ea03102ff12293b0d4d47ea6a9a0 Mon Sep 17 00:00:00 2001 From: Microsoft GitHub User Date: Tue, 18 Aug 2015 18:11:19 -0700 Subject: Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..6948aae --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# GSL +Guidelines Support Library -- cgit v1.2.3 From a9dcbe04ff330ef8297191d19951d4a313b2115a Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 20 Aug 2015 18:09:14 -0700 Subject: Initial commit of library files. --- .gitignore | 1 + CMakeLists.txt | 11 + LICENSE | 11 + README.md | 64 +- include/array_view.h | 2113 +++++++++++++++++++++++++++++++++++++++++++ include/fail_fast.h | 39 + include/gsl.h | 284 ++++++ include/string_view.h | 178 ++++ tests/CMakeLists.txt | 143 +++ tests/array_view_tests.cpp | 648 +++++++++++++ tests/assertion_tests.cpp | 53 ++ tests/at_tests.cpp | 60 ++ tests/bounds_tests.cpp | 99 ++ tests/maybenull_tests.cpp | 197 ++++ tests/notnull_tests.cpp | 87 ++ tests/string_view_tests.cpp | 104 +++ tests/utils_tests.cpp | 87 ++ 17 files changed, 4177 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 include/array_view.h create mode 100644 include/fail_fast.h create mode 100644 include/gsl.h create mode 100644 include/string_view.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/array_view_tests.cpp create mode 100644 tests/assertion_tests.cpp create mode 100644 tests/at_tests.cpp create mode 100644 tests/bounds_tests.cpp create mode 100644 tests/maybenull_tests.cpp create mode 100644 tests/notnull_tests.cpp create mode 100644 tests/string_view_tests.cpp create mode 100644 tests/utils_tests.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f5de3e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tests/unittest-cpp \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2125f7b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.2.2) + +project(GSL) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} +) + +enable_testing() + +add_subdirectory(tests) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..011f2f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +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. diff --git a/README.md b/README.md index 6948aae..c0f100e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,62 @@ -# GSL -Guidelines Support Library +# GSL: Guidelines Support Library + +This library contains functions and types that are suggested for use by the +[C++ Coding Guidelines](https://github.com/Microsoft/CppCodingStandards/). + +These include types like `array_view<>`, `string_view<>`, `owner<>` and others. + +The entire implementation is provided inline in the headers under the [include](./include) directory. + +While some types have been broken out into their own headers (e.g. [include/array_view.h](./include/array_view.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. + +# Quick Start +## Supported Platforms +The test suite that exercises GSL has been built and passes successfully on the following platforms: + +* Windows using Visual Studio 2013 +* Windows using Visual Studio 2015 +* Windows using Clang\LLVM 3.6 +* Windows using GCC 5.1 +* Linux using Clang\LLVM 3.6 +* Linux using GCC 5.1 + +> If you successfully port GSL to another platform, we would love to hear from you. Please consider contributing +any changes that were necessary back to this project to benefit the wider community. + +## Building the tests +To build the tests, you will require the following: + +* [CMake](http://cmake.org), version 3.3 or later to be installed and in your PATH. +* [UnitTest-cpp](https://github.com/Microsoft/unittest-cpp), to be cloned under the [tests/unittest-cpp](./tests/unittest-cpp) directory +of your GSL source. + +These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`. + +1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\vs14-x86 in this example). + + cd GSL + md build-x86 + cd build-x86 + +2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`). + + cmake -G "Visual Studio 14 2015" c:\GSL + +3. Build the test suite (in this case, in the Debug configuration, Release is another good choice). + + cmake --build . --config Debug + +4. Run the test suite. + + ctest -C Debug + +All tests should pass - indicating your platform is fully supported and you are ready to use the GSL types! + +## Using the libraries +As the types are entirely implemented inline in headers, there are no linking requirements. + +Just place the contents of the [include](./include) directory within your source tree so it is available +to your compiler, then include the appropriate headers in your program, and away you go! diff --git a/include/array_view.h b/include/array_view.h new file mode 100644 index 0000000..ecc3d1e --- /dev/null +++ b/include/array_view.h @@ -0,0 +1,2113 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fail_fast.h" + +#ifndef _MSC_VER +#define _CONSTEXPR constexpr +#else +#define _CONSTEXPR +#endif + +#pragma push_macro("_NOEXCEPT") + +#ifndef _NOEXCEPT + +#ifdef SAFER_CPP_TESTING +#define _NOEXCEPT +#else +#define _NOEXCEPT noexcept +#endif + +#else // _NOEXCEPT + +#ifdef SAFER_CPP_TESTING +#undef _NOEXCEPT +#define _NOEXCEPT +#endif + +#endif // _NOEXCEPT + +namespace Guide { + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + }; + + + template + class coordinate_facade + { + static_assert(std::is_integral::value + && sizeof(ValueType) <= sizeof(size_t), "ValueType must be unsigned integral type!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class coordinate_facade; + public: + using reference = ValueType&; + using const_reference = const ValueType&; + using value_type = ValueType; + static const unsigned int rank = Rank; + _CONSTEXPR coordinate_facade() _NOEXCEPT + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + for (unsigned int i = 0; i < rank; ++i) + elems[i] = {}; + } + _CONSTEXPR coordinate_facade(value_type e0) _NOEXCEPT + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + static_assert(rank == 1, "This constructor can only be used with rank == 1."); + elems[0] = e0; + } + // Preconditions: il.size() == rank + _CONSTEXPR coordinate_facade(std::initializer_list il) + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + fail_fast_assert(il.size() == rank); + for (unsigned int i = 0; i < rank; ++i) + { + elems[i] = begin(il)[i]; + } + } + + _CONSTEXPR coordinate_facade(const coordinate_facade & other) = default; + + template + _CONSTEXPR coordinate_facade(const coordinate_facade & other) + { + for (unsigned int i = 0; i < rank; ++i) + { + fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); + elems[i] = static_cast(other.elems[i]); + } + } + protected: + coordinate_facade& operator=(const coordinate_facade& rhs) = default; + // Preconditions: component_idx < rank + _CONSTEXPR reference operator[](unsigned int component_idx) + { + fail_fast_assert(component_idx < rank); + return elems[component_idx]; + } + // Preconditions: component_idx < rank + _CONSTEXPR const_reference operator[](unsigned int component_idx) const + { + fail_fast_assert(component_idx < rank); + return elems[component_idx]; + } + _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (elems[i] != rhs.elems[i]) + return false; + } + return true; + } + _CONSTEXPR bool operator!=(const ConcreteType& rhs) const _NOEXCEPT + { + return !(to_concrete() == rhs); + } + _CONSTEXPR ConcreteType operator+() const _NOEXCEPT + { + return to_concrete(); + } + _CONSTEXPR ConcreteType operator-() const + { + ConcreteType ret = to_concrete(); + for (unsigned int i = 0; i < rank; ++i) + ret.elems[i] = -ret.elems[i]; + return ret; + } + _CONSTEXPR ConcreteType operator+(const ConcreteType& rhs) const + { + ConcreteType ret = to_concrete(); + ret += rhs; + return ret; + } + _CONSTEXPR ConcreteType operator-(const ConcreteType& rhs) const + { + ConcreteType ret = to_concrete(); + ret -= rhs; + return ret; + } + _CONSTEXPR ConcreteType& operator+=(const ConcreteType& rhs) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] += rhs.elems[i]; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator-=(const ConcreteType& rhs) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] -= rhs.elems[i]; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator++() + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ++elems[0]; + return to_concrete(); + } + _CONSTEXPR ConcreteType operator++(int) + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ConcreteType ret = to_concrete(); + ++(*this); + return ret; + } + _CONSTEXPR ConcreteType& operator--() + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + --elems[0]; + return to_concrete(); + } + _CONSTEXPR ConcreteType operator--(int) + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ConcreteType ret = to_concrete(); + --(*this); + return ret; + } + _CONSTEXPR ConcreteType operator*(value_type v) const + { + ConcreteType ret = to_concrete(); + ret *= v; + return ret; + } + _CONSTEXPR ConcreteType operator/(value_type v) const + { + ConcreteType ret = to_concrete(); + ret /= v; + return ret; + } + friend _CONSTEXPR ConcreteType operator*(value_type v, const ConcreteType& rhs) + { + return rhs * v; + } + _CONSTEXPR ConcreteType& operator*=(value_type v) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] *= v; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator/=(value_type v) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] /= v; + return to_concrete(); + } + value_type elems[rank]; + private: + _CONSTEXPR const ConcreteType& to_concrete() const _NOEXCEPT + { + return static_cast(*this); + } + _CONSTEXPR ConcreteType& to_concrete() _NOEXCEPT + { + return static_cast(*this); + } + }; + template + class arrow_proxy + { + public: + explicit arrow_proxy(T t) + : val(t) + {} + const T operator*() const _NOEXCEPT + { + return val; + } + const T* operator->() const _NOEXCEPT + { + return &val; + } + private: + T val; + }; +} + +template +class index : private details::coordinate_facade, ValueType, Rank> +{ + using Base = details::coordinate_facade, ValueType, Rank>; + friend Base; + +public: + using Base::rank; + using reference = typename Base::reference; + using const_reference = typename Base::const_reference; + using size_type = typename Base::value_type; + using value_type = typename Base::value_type; + _CONSTEXPR index() _NOEXCEPT : Base(){} + template > + _CONSTEXPR index(value_type e0) _NOEXCEPT : Base(e0){} + _CONSTEXPR index(std::initializer_list il) : Base(il){} + + _CONSTEXPR index(const index &) = default; + + template + _CONSTEXPR index(const index &other) : Base(other) + { + } + + using Base::operator[]; + using Base::operator==; + using Base::operator!=; + using Base::operator+; + using Base::operator-; + using Base::operator+=; + using Base::operator-=; + using Base::operator++; + using Base::operator--; + using Base::operator*; + using Base::operator/; + using Base::operator*=; + using Base::operator/=; +}; + +template +class index<1, ValueType> +{ + template + friend class index; +public: + static const unsigned int rank = 1; + using reference = ValueType&; + using const_reference = const ValueType&; + using size_type = ValueType; + using value_type = ValueType; + + _CONSTEXPR index() _NOEXCEPT : value(0) + { + } + _CONSTEXPR index(value_type e0) _NOEXCEPT : value(e0) + { + } + // Preconditions: il.size() == rank + _CONSTEXPR index(std::initializer_list il) + { + fail_fast_assert(il.size() == rank); + value = begin(il)[0]; + } + + _CONSTEXPR index(const index &) = default; + + template + _CONSTEXPR index(const index<1, OtherValueType> & other) + { + fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); + value = static_cast(other.value); + } + + + // Preconditions: component_idx < rank + _CONSTEXPR reference operator[](size_type component_idx) _NOEXCEPT + { + fail_fast_assert(component_idx == 0); + (void)(component_idx); + return value; + } + // Preconditions: component_idx < rank + _CONSTEXPR const_reference operator[](size_type component_idx) const _NOEXCEPT + { + fail_fast_assert(component_idx == 0); + (void)(component_idx); + return value; + } + _CONSTEXPR bool operator==(const index& rhs) const _NOEXCEPT + { + return value == rhs.value; + } + _CONSTEXPR bool operator!=(const index& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + _CONSTEXPR index operator+() const _NOEXCEPT + { + return *this; + } + _CONSTEXPR index operator-() const _NOEXCEPT + { + return index(-value); + } + _CONSTEXPR index operator+(const index& rhs) const _NOEXCEPT + { + return index(value + rhs.value); + } + _CONSTEXPR index operator-(const index& rhs) const _NOEXCEPT + { + return index(value - rhs.value); + } + _CONSTEXPR index& operator+=(const index& rhs) _NOEXCEPT + { + value += rhs.value; + return *this; + } + _CONSTEXPR index& operator-=(const index& rhs) _NOEXCEPT + { + value -= rhs.value; + return *this; + } + _CONSTEXPR index& operator++() _NOEXCEPT + { + ++value; + return *this; + } + _CONSTEXPR index operator++(int) _NOEXCEPT + { + index ret = *this; + ++(*this); + return ret; + } + _CONSTEXPR index& operator--() _NOEXCEPT + { + --value; + return *this; + } + _CONSTEXPR index operator--(int) _NOEXCEPT + { + index ret = *this; + --(*this); + return ret; + } + _CONSTEXPR index operator*(value_type v) const _NOEXCEPT + { + return index(value * v); + } + _CONSTEXPR index operator/(value_type v) const _NOEXCEPT + { + return index(value / v); + } + _CONSTEXPR index& operator*=(value_type v) _NOEXCEPT + { + value *= v; + return *this; + } + _CONSTEXPR index& operator/=(value_type v) _NOEXCEPT + { + value /= v; + return *this; + } + friend _CONSTEXPR index operator*(value_type v, const index& rhs) _NOEXCEPT + { + return index(rhs * v); + } +private: + value_type value; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return static_cast(-1); + } + + template ::value>> + constexpr bool operator ==(T other) const noexcept + { + return static_cast(-1) == other; + } + + template ::value>> + constexpr bool operator !=(T other) const noexcept + { + return static_cast(-1) != other; + } + +}; + +template ::value>> +constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const char dynamic_range = -1; +#endif + +struct generalized_mapping_tag {}; +struct contiguous_mapping_tag : generalized_mapping_tag {}; + +namespace details +{ + template + struct StaticSizeHelperImpl + { + static_assert(static_cast(Fact1) * static_cast(Fact2) <= SizeTypeTraits::max_value, "Value out of the range of SizeType"); + static const SizeType value = Fact1 * Fact2; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = ConstBound; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = ConstBound; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = static_cast(ConstBound); + }; + + template + struct StaticSizeHelper + { + static const SizeType value = StaticSizeHelperImpl(Fact1), static_cast(Fact2), static_cast(dynamic_range)>::value; + }; + + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges { + static const unsigned int Depth = 0; + static const unsigned int DynamicNum = 0; + static const SizeType CurrentRange = 1; + static const SizeType TotalSize = 1; + + BoundsRanges (const BoundsRanges &) = default; + + // TODO : following signature is for work around VS bug + template + BoundsRanges (const OtherType &, bool firstLevel) {} + BoundsRanges(const SizeType * const arr) { } + BoundsRanges() = default; + + + template + void serialize(T &) const { + } + template + SizeType linearize(const T &) const { + return 0; + } + template + ptrdiff_t contains(const T &) const { + return 0; + } + + size_t totalSize() const _NOEXCEPT { + return TotalSize; + } + + bool operator == (const BoundsRanges &) const _NOEXCEPT + { + return true; + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + static const unsigned int Depth = Base::Depth + 1; + static const unsigned int DynamicNum = Base::DynamicNum + 1; + static const SizeType CurrentRange = dynamic_range; + static const SizeType TotalSize = dynamic_range; + const SizeType m_bound; + + BoundsRanges (const BoundsRanges &) = default; + BoundsRanges(const SizeType * const arr) : Base(arr + 1), m_bound(static_cast(*arr * this->Base::totalSize())) + { + fail_fast_assert(0 <= *arr); + fail_fast_assert(*arr * this->Base::totalSize() <= details::SizeTypeTraits::max_value); + } + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : + Base(static_cast&>(other), false), m_bound (static_cast(other.totalSize())) + { + } + + template + void serialize(T & arr) const { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + template + SizeType linearize(const T & arr) const { + const size_t index = this->Base::totalSize() * arr[Dim]; + fail_fast_assert(index < static_cast(m_bound)); + return static_cast(index) + this->Base::template linearize(arr); + } + + template + ptrdiff_t contains(const T & arr) const { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return static_cast(cur) < static_cast(m_bound) ? cur + last : -1; + } + + size_t totalSize() const _NOEXCEPT { + return m_bound; + } + + SizeType elementNum() const _NOEXCEPT { + return static_cast(totalSize() / this->Base::totalSize()); + } + + SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + { + return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + static const unsigned int Depth = Base::Depth + 1; + static const unsigned int DynamicNum = Base::DynamicNum; + static const SizeType CurrentRange = static_cast(CurRange); + static const SizeType TotalSize = StaticSizeHelper::value; + static_assert (CurRange <= SizeTypeTraits::max_value, "CurRange must be smaller than SizeType limits"); + + BoundsRanges (const BoundsRanges &) = default; + BoundsRanges(const SizeType * const arr) : Base(arr) { } + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : Base(static_cast&>(other), false) + { + fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + } + + template + void serialize(T & arr) const { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + SizeType linearize(const T & arr) const { + fail_fast_assert(arr[Dim] < CurrentRange); + return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); + } + + template + ptrdiff_t contains(const T & arr) const { + if (static_cast(arr[Dim]) >= CurrentRange) + return -1; + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + return static_cast(this->Base::totalSize() * arr[Dim]) + last; + } + + size_t totalSize() const _NOEXCEPT{ + return CurrentRange * this->Base::totalSize(); + } + + SizeType elementNum() const _NOEXCEPT{ + return CurrentRange; + } + + SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible2; + + // TODO: I have to rewrite BoundsRangeConvertible into following way to workaround VS 2013 bugs + template > + auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant())) + {}; + + template + struct BoundsRangeConvertible2 : std::true_type {}; + + template + struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) + {}; + template + struct BoundsRangeConvertible : std::true_type {}; + + template + struct TypeListIndexer + { + const TypeChain & obj; + TypeListIndexer(const TypeChain & obj) :obj(obj){} + template + const TypeChain & getObj(std::true_type) + { + return obj; + } + template + auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) + { + return TypeListIndexer(static_cast(obj)).template get(); + } + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain &obj) + { + return TypeListIndexer(obj); + } +} + +template +class bounds_iterator; + +template +class static_bounds { +public: + static_bounds(const details::BoundsRanges &empty) { + } +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges ; + static_assert(std::is_integral::value + && details::SizeTypeTraits::max_value <= SIZE_MAX, "SizeType must be an integral type and its numeric limits must be smaller than SIZE_MAX"); + + MyRanges m_ranges; + _CONSTEXPR static_bounds(const MyRanges & range) : m_ranges(range) { } + + template + friend class static_bounds; +public: + static const unsigned int rank = MyRanges::Depth; + static const unsigned int dynamic_rank = MyRanges::DynamicNum; + static const SizeType static_size = static_cast(MyRanges::TotalSize); + + using size_type = SizeType; + using index_type = index; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; +public: + _CONSTEXPR static_bounds(const static_bounds &) = default; + + template , details::BoundsRanges >::value>> + _CONSTEXPR static_bounds(const static_bounds &other): + m_ranges(other.m_ranges) + { + } + + _CONSTEXPR static_bounds(std::initializer_list il) : m_ranges(il.begin()) + { + fail_fast_assert(MyRanges::DynamicNum == il.size() && m_ranges.totalSize() <= details::SizeTypeTraits::max_value); + } + + _CONSTEXPR static_bounds() = default; + + _CONSTEXPR static_bounds & operator = (const static_bounds & otherBounds) + { + new(&m_ranges) MyRanges (otherBounds.m_ranges); + return *this; + } + + _CONSTEXPR sliced_type slice() const _NOEXCEPT + { + return sliced_type{static_cast &>(m_ranges)}; + } + + _CONSTEXPR size_type stride() const _NOEXCEPT + { + return rank > 1 ? slice().size() : 1; + } + + _CONSTEXPR size_type size() const _NOEXCEPT + { + return static_cast(m_ranges.totalSize()); + } + + _CONSTEXPR size_type linearize(const index_type & idx) const + { + return m_ranges.linearize(idx); + } + + _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + { + return m_ranges.contains(idx) != -1; + } + + _CONSTEXPR size_type operator[](unsigned int index) const _NOEXCEPT + { + return m_ranges.elementNum(index); + } + + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + _CONSTEXPR index_type index_bounds() const _NOEXCEPT + { + index_type extents; + m_ranges.serialize(extents); + return extents; + } + + template + _CONSTEXPR bool operator == (const static_bounds & rhs) const _NOEXCEPT + { + return this->size() == rhs.size(); + } + + template + _CONSTEXPR bool operator != (const static_bounds & rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + + _CONSTEXPR const_iterator begin() const _NOEXCEPT + { + return const_iterator(*this); + } + + _CONSTEXPR const_iterator end() const _NOEXCEPT + { + index_type boundary; + m_ranges.serialize(boundary); + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds : private details::coordinate_facade, SizeType, Rank> +{ + using Base = details::coordinate_facade, SizeType, Rank>; + friend Base; + _CONSTEXPR void makeRegularStriae() _NOEXCEPT + { + strides[rank - 1] = 1; + for (int i = rank - 2; i >= 0; i--) + strides[i] = strides[i + 1] * Base::elems[i + 1]; + } +public: + using Base::rank; + using reference = typename Base::reference; + using const_reference = typename Base::const_reference; + using size_type = typename Base::value_type; + using difference_type = typename Base::value_type; + using value_type = typename Base::value_type; + using index_type = index; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const int dynamic_rank = rank; + static const size_t static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + _CONSTEXPR strided_bounds() _NOEXCEPT : Base(), strides() {} + _CONSTEXPR strided_bounds(const strided_bounds &) = default; + + template + _CONSTEXPR strided_bounds(const strided_bounds &other) : Base(other) + { + } + + _CONSTEXPR strided_bounds(const index_type &extents, const index_type &stride) + : strides(stride) + { + for (unsigned int i = 0; i < rank; i++) + Base::elems[i] = extents[i]; + } + _CONSTEXPR strided_bounds(std::initializer_list il) + : Base(il) + { +#ifndef NDEBUG + for (const auto& v : il) + { + fail_fast_assert(v >= 0); + } +#endif + makeRegularStriae(); + } + index_type strides; + _CONSTEXPR size_type size() const _NOEXCEPT + { + size_type ret = 0; + for (unsigned int i = 0; i < rank; ++i) + ret += (Base::elems[i] - 1) * strides[i]; + return ret; + } + _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (idx[i] < 0 || idx[i] >= Base::elems[i]) + return false; + } + return true; + } + _CONSTEXPR size_type linearize(const index_type & idx) const + { + size_type ret = 0; + for (unsigned int i = 0; i < rank; i++) + { + fail_fast_assert(idx[i] < Base::elems[i]); + ret += idx[i] * strides[i]; + } + return ret; + } + _CONSTEXPR sliced_type slice() const + { + sliced_type ret; + for (unsigned int i = 1; i < rank; ++i) + { + ret.elems[i - 1] = Base::elems[i]; + ret.strides[i - 1] = strides[i]; + } + return ret; + } + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return Base::elems[Dim]; + } + _CONSTEXPR index_type index_bounds() const _NOEXCEPT + { + index_type extents; + for (unsigned int i = 0; i < rank; ++i) + extents[i] = (*this)[i]; + return extents; + } + const_iterator begin() const _NOEXCEPT + { + return const_iterator{ *this }; + } + const_iterator end() const _NOEXCEPT + { + return const_iterator{ *this, index_bounds() }; + } +}; + +template +struct is_bounds : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; + +template +class bounds_iterator + : public std::iterator, + const IndexType> +{ +private: + using Base = std::iterator , const IndexType>; +public: + static const unsigned int rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::size_type; + template + explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) _NOEXCEPT + : boundary(bnd.index_bounds()) + , curr( std::move(curr) ) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + reference operator*() const _NOEXCEPT + { + return curr; + } + pointer operator->() const _NOEXCEPT + { + return details::arrow_proxy{ curr }; + } + bounds_iterator& operator++() _NOEXCEPT + { + for (unsigned int i = rank; i-- > 0;) + { + if (++curr[i] < boundary[i]) + { + return *this; + } + else + { + curr[i] = 0; + } + } + // If we're here we've wrapped over - set to past-the-end. + for (unsigned int i = 0; i < rank; ++i) + { + curr[i] = boundary[i]; + } + return *this; + } + bounds_iterator operator++(int) _NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + bounds_iterator& operator--() _NOEXCEPT + { + for (int i = rank; i-- > 0;) + { + if (curr[i]-- > 0) + { + return *this; + } + else + { + curr[i] = boundary[i] - 1; + } + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + fail_fast_assert(false); + return *this; + } + bounds_iterator operator--(int) _NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + bounds_iterator operator+(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret += n; + } + bounds_iterator& operator+=(difference_type n) _NOEXCEPT + { + auto linear_idx = linearize(curr) + n; + value_type stride; + stride[rank - 1] = 1; + for (unsigned int i = rank - 1; i-- > 0;) + { + stride[i] = stride[i + 1] * boundary[i + 1]; + } + for (unsigned int i = 0; i < rank; ++i) + { + curr[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + return *this; + } + bounds_iterator operator-(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret -= n; + } + bounds_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + { + return linearize(curr) - linearize(rhs.curr); + } + reference operator[](difference_type n) const _NOEXCEPT + { + return *(*this + n); + } + bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr == rhs.curr; + } + bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (curr[i] < rhs.curr[i]) + return true; + } + return false; + } + bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(bounds_iterator& rhs) _NOEXCEPT + { + std::swap(boundary, rhs.boundary); + std::swap(curr, rhs.curr); + } +private: + index_size_type linearize(const value_type& idx) const _NOEXCEPT + { + // TODO: Smarter impl. + // Check if past-the-end + bool pte = true; + for (unsigned int i = 0; i < rank; ++i) + { + if (idx[i] != boundary[i]) + { + pte = false; + break; + } + } + index_size_type multiplier = 1; + index_size_type res = 0; + if (pte) + { + res = 1; + for (unsigned int i = rank; i-- > 0;) + { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary[i]; + } + } + else + { + for (unsigned int i = rank; i-- > 0;) + { + res += idx[i] * multiplier; + multiplier *= boundary[i]; + } + } + return res; + } + value_type boundary; + value_type curr; +}; + +template +class bounds_iterator> + : public std::iterator, + ptrdiff_t, + const details::arrow_proxy>, + const index<1, SizeType>> +{ + using Base = std::iterator, ptrdiff_t, const details::arrow_proxy>, const index<1, SizeType>>; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename index_type::size_type; + + template + explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) _NOEXCEPT + : curr( std::move(curr) ) + {} + reference operator*() const _NOEXCEPT + { + return curr; + } + pointer operator->() const _NOEXCEPT + { + return details::arrow_proxy{ curr }; + } + bounds_iterator& operator++() _NOEXCEPT + { + ++curr; + return *this; + } + bounds_iterator operator++(int) _NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + bounds_iterator& operator--() _NOEXCEPT + { + curr--; + return *this; + } + bounds_iterator operator--(int) _NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + bounds_iterator operator+(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret += n; + } + bounds_iterator& operator+=(difference_type n) _NOEXCEPT + { + curr += n; + return *this; + } + bounds_iterator operator-(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret -= n; + } + bounds_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr[0] - rhs.curr[0]; + } + reference operator[](difference_type n) const _NOEXCEPT + { + return curr + n; + } + bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr == rhs.curr; + } + bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr[0] < rhs.curr[0]; + } + bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(bounds_iterator& rhs) _NOEXCEPT + { + std::swap(curr, rhs.curr); + } +private: + value_type curr; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +/* +** begin definitions of basic_array_view +*/ +namespace details +{ + template + _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + { + return bnd.strides; + } + + // Make a stride vector from bounds, assuming continugous memory. + template + _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + { + auto extents = bnd.index_bounds(); + typename Bounds::index_type stride; + stride[Bounds::rank - 1] = 1; + for (int i = Bounds::rank - 2; i >= 0; --i) + stride[i] = stride[i + 1] * extents[i + 1]; + return stride; + } + + template + void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) + { + static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); + static_assert(std::is_same::value, "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); + fail_fast_assert(src.size() == dest.size()); + } + + +} // namespace details + +template +class contiguous_array_view_iterator; +template +class general_array_view_iterator; +enum class byte : std::uint8_t {}; + +template +class basic_array_view +{ +public: + static const unsigned int rank = BoundsType::rank; + using bounds_type = BoundsType; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using pointer = ValueType*; + using reference = ValueType&; + using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; + +private: + pointer m_pdata; + bounds_type m_bounds; + +public: + _CONSTEXPR bounds_type bounds() const _NOEXCEPT + { + return m_bounds; + } + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return m_bounds.template extent(); + } + _CONSTEXPR size_type size() const _NOEXCEPT + { + return m_bounds.size(); + } + _CONSTEXPR reference operator[](const index_type& idx) const + { + return m_pdata[m_bounds.linearize(idx)]; + } + _CONSTEXPR pointer data() const _NOEXCEPT + { + return m_pdata; + } + template > + _CONSTEXPR Ret operator[](size_type idx) const + { + const size_type ridx = idx * m_bounds.stride(); + fail_fast_assert(ridx < m_bounds.size()); + return Ret {m_pdata + ridx, m_bounds.slice()}; + } + + _CONSTEXPR operator bool () const _NOEXCEPT + { + return m_pdata != nullptr; + } + + _CONSTEXPR iterator begin() const + { + return iterator {this, true}; + } + _CONSTEXPR iterator end() const + { + return iterator {this}; + } + _CONSTEXPR const_iterator cbegin() const + { + return const_iterator {reinterpret_cast *>(this), true}; + } + _CONSTEXPR const_iterator cend() const + { + return const_iterator {reinterpret_cast *>(this)}; + } + + _CONSTEXPR reverse_iterator rbegin() const + { + return reverse_iterator {end()}; + } + _CONSTEXPR reverse_iterator rend() const + { + return reverse_iterator {begin()}; + } + _CONSTEXPR const_reverse_iterator crbegin() const + { + return const_reverse_iterator {cend()}; + } + _CONSTEXPR const_reverse_iterator crend() const + { + return const_reverse_iterator {cbegin()}; + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR auto operator == (const basic_array_view & other) const -> decltype(this->m_pdata == other.m_pdata && this->m_bounds == other.m_bounds) _NOEXCEPT + { + return m_pdata == other.m_pdata && m_bounds == other.m_bounds; + } + +public: + template ::value + && std::is_convertible::value>> + _CONSTEXPR basic_array_view(const basic_array_view & other ) _NOEXCEPT + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) + { + } +protected: + + _CONSTEXPR basic_array_view(pointer data, bounds_type bound) _NOEXCEPT + : m_pdata(data) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + _CONSTEXPR basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) _NOEXCEPT + : m_pdata(reinterpret_cast(data)) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + _CONSTEXPR basic_array_view as_array_view(const DestBounds &bounds) + { + details::verifyBoundsReshape(m_bounds, bounds); + return {m_pdata, bounds}; + } +private: + + friend iterator; + friend const_iterator; + template + friend class basic_array_view; +}; + +template +struct dim +{ + static const size_t value = DimSize; +}; +template <> +struct dim +{ + static const size_t value = dynamic_range; + const size_t dvalue; + dim(size_t size) : dvalue(size) {} +}; + +template +class array_view; +template +class strided_array_view; + +namespace details +{ + template + struct ArrayViewTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct ArrayViewTypeTraits::type> + { + using value_type = typename Traits::array_view_traits::value_type; + using size_type = typename Traits::array_view_traits::size_type; + }; + + template + struct ArrayViewArrayTraits { + using type = array_view; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + + template + BoundsType newBoundsHelperImpl(size_t totalSize, std::true_type) // dynamic size + { + fail_fast_assert(totalSize <= details::SizeTypeTraits::max_value); + return BoundsType{static_cast(totalSize)}; + } + template + BoundsType newBoundsHelperImpl(size_t totalSize, std::false_type) // static size + { + fail_fast_assert(BoundsType::static_size == totalSize); + return {}; + } + template + BoundsType newBoundsHelper(size_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl(totalSize, std::integral_constant()); + } + + struct Sep{}; + + template + T static_as_array_view_helper(Sep, Args... args) + { + return T{static_cast(args)...}; + } + template + std::enable_if_t>::value && !std::is_same::value, T> static_as_array_view_helper(Arg, Args... args) + { + return static_as_array_view_helper(args...); + } + template + T static_as_array_view_helper(dim val, Args ... args) + { + return static_as_array_view_helper(args..., val.dvalue); + } + + template + struct static_as_array_view_static_bounds_helper + { + using type = static_bounds; + }; + + template + struct is_array_view_oracle : std::false_type + {}; + template + struct is_array_view_oracle> : std::true_type + {}; + template + struct is_array_view_oracle> : std::true_type + {}; + template + struct is_array_view : is_array_view_oracle> + {}; + +} + + +template +struct array_view_options +{ + struct array_view_traits + { + using value_type = ValueType; + using size_type = SizeType; + }; +}; + +template +class array_view : public basic_array_view::value_type, + static_bounds::size_type, FirstDimension, RestDimensions...>> +{ + template + friend class array_view; + using Base = basic_array_view::value_type, + static_bounds::size_type, FirstDimension, RestDimensions...>>; + +public: + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using Base::rank; + +public: + // basic + _CONSTEXPR array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + { + } + + _CONSTEXPR array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) + { + } + + _CONSTEXPR array_view(nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + { + fail_fast_assert(size == 0); + } + + // default + template > + _CONSTEXPR array_view() : Base(nullptr, bounds_type()) + { + } + + // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value>> + _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) + { + } + + // from n-dimensions static array + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value>> + _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + { + } + + // from n-dimensions static array with size + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value >> + _CONSTEXPR array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) + { + fail_fast_assert(size <= N); + } + + // from std array + template , typename Base::bounds_type>::value>> + _CONSTEXPR array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) + { + } + + template , typename Base::bounds_type>::value && std::is_const::value>> + _CONSTEXPR array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + { + } + + + // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity + template ::value + && details::LessThan::value>> // remove literal 0 case + _CONSTEXPR array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + { + } + + // from containers. It must has .size() and .data() two function signatures + template ::value + && std::is_convertible::value + && std::is_convertible, typename Base::bounds_type>::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + _CONSTEXPR array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + { + + } + + _CONSTEXPR array_view(const array_view &) = default; + + // convertible + template ::value_type, static_bounds::size_type, FirstDimension, RestDimensions...>>, + typename OtherBaseType = basic_array_view::value_type, static_bounds::size_type, OtherDimensions...>>, + typename Dummy = std::enable_if_t::value> + > + _CONSTEXPR array_view(const array_view &av) : Base(static_cast::Base &>(av)) {} // static_cast is required + + // reshape + template + _CONSTEXPR array_view as_array_view(Dimensions2... dims) + { + static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); + using BoundsType = typename array_view::bounds_type; + auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(this->bounds(), tobounds); + return {this->data(), tobounds}; + } + + // to bytes array + template ::value_type>>::value> + _CONSTEXPR auto as_bytes() const _NOEXCEPT -> + array_view, static_cast(details::StaticSizeHelper::value)> + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + template ::value_type>>::value> + _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT -> + array_view, static_cast(details::StaticSizeHelper::value)> + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + + // from bytes array + template::value, typename Dummy = std::enable_if_t> + _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + } + + template::value, typename Dummy = std::enable_if_t> + _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + } + + // section on linear space + template + _CONSTEXPR array_view first() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + return { this->data(), Count }; + } + + _CONSTEXPR array_view first(size_type count) const _NOEXCEPT + { + fail_fast_assert(count <= this->size()); + return { this->data(), count }; + } + + template + _CONSTEXPR array_view last() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + return { this->data() + this->size() - Count, Count }; + } + + _CONSTEXPR array_view last(size_type count) const _NOEXCEPT + { + fail_fast_assert(count <= this->size()); + return { this->data() + this->size() - count, count }; + } + + template + _CONSTEXPR array_view sub() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset < bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset < this->size()) && Offset + Count <= this->size())); + return { this->data() + Offset, Count }; + } + + _CONSTEXPR array_view sub(size_type offset, size_type count) const _NOEXCEPT + { + fail_fast_assert((offset == 0 || offset < this->size()) && offset + count <= this->size()); + return { this->data() + offset, count }; + } + + // size + _CONSTEXPR size_type length() const _NOEXCEPT + { + return this->size(); + } + _CONSTEXPR size_type used_length() const _NOEXCEPT + { + return length(); + } + _CONSTEXPR size_type bytes() const _NOEXCEPT + { + return sizeof(value_type) * this->size(); + } + _CONSTEXPR size_type used_bytes() const _NOEXCEPT + { + return bytes(); + } + + // section + _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const + { + return { &this->operator[](origin), strided_bounds {extents, details::make_stride(Base::bounds())}}; + } +}; + +template +_CONSTEXPR auto as_array_view(T * const & ptr, dim... args) -> array_view, Dimensions...> +{ + return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; +} + +template +_CONSTEXPR auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits::type +{ + return {arr, len}; +} + +template +_CONSTEXPR auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(const std::array &arr) +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(const std::array &&) = delete; + +template +_CONSTEXPR array_view as_array_view(std::array &arr) +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(T *begin, T *end) +{ + return {begin, end}; +} + +template +_CONSTEXPR auto as_array_view(Cont &arr) -> std::enable_if_t>::value, + array_view, dynamic_range>> +{ + return {arr.data(), arr.size()}; +} + +template +_CONSTEXPR auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, + array_view, dynamic_range>> = delete; + +template +class strided_array_view : public basic_array_view::value_type, strided_bounds::size_type>> +{ + using Base = basic_array_view::value_type, strided_bounds::size_type>>; +public: + using Base::rank; + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + + strided_array_view (pointer ptr, bounds_type bounds): Base(ptr, std::move(bounds)) + { + } + template > + strided_array_view (array_view av, index_type strides): Base(av.data(), bounds_type{av.bounds().index_bounds(), strides}) + { + } + // section + strided_array_view section(index_type origin, index_type extents) const + { + return { &this->operator[](origin), bounds_type {extents, details::make_stride(Base::bounds())}}; + } +}; + +template +class contiguous_array_view_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; +private: + template + friend class basic_array_view; + pointer m_pdata; + const ArrayView * m_validator; + void validateThis() const + { + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size()); + } + contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } +public: + reference operator*() const _NOEXCEPT + { + validateThis(); + return *m_pdata; + } + pointer operator->() const _NOEXCEPT + { + validateThis(); + return m_pdata; + } + contiguous_array_view_iterator& operator++() _NOEXCEPT + { + ++m_pdata; + return *this; + } + contiguous_array_view_iterator operator++(int)_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_array_view_iterator& operator--() _NOEXCEPT + { + --m_pdata; + return *this; + } + contiguous_array_view_iterator operator--(int)_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_array_view_iterator operator+(difference_type n) const _NOEXCEPT + { + contiguous_array_view_iterator ret{ *this }; + return ret += n; + } + contiguous_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + { + m_pdata += n; + return *this; + } + contiguous_array_view_iterator operator-(difference_type n) const _NOEXCEPT + { + contiguous_array_view_iterator ret{ *this }; + return ret -= n; + } + contiguous_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata - rhs.m_pdata; + } + reference operator[](difference_type n) const _NOEXCEPT + { + return *(*this + n); + } + bool operator==(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata == rhs.m_pdata; + } + bool operator!=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata < rhs.m_pdata; + } + bool operator<=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(contiguous_array_view_iterator& rhs) _NOEXCEPT + { + std::swap(m_pdata, rhs.m_pdata); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +template +class general_array_view_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; +private: + template + friend class basic_array_view; + const ArrayView * m_container; + typename ArrayView::iterator m_itr; + general_array_view_iterator(const ArrayView *container, bool isbegin = false) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } +public: + reference operator*() const _NOEXCEPT + { + return (*m_container)[*m_itr]; + } + pointer operator->() const _NOEXCEPT + { + return &(*m_container)[*m_itr]; + } + general_array_view_iterator& operator++() _NOEXCEPT + { + ++m_itr; + return *this; + } + general_array_view_iterator operator++(int)_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + general_array_view_iterator& operator--() _NOEXCEPT + { + --m_itr; + return *this; + } + general_array_view_iterator operator--(int)_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + general_array_view_iterator operator+(difference_type n) const _NOEXCEPT + { + general_array_view_iterator ret{ *this }; + return ret += n; + } + general_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + { + m_itr += n; + return *this; + } + general_array_view_iterator operator-(difference_type n) const _NOEXCEPT + { + general_array_view_iterator ret{ *this }; + return ret -= n; + } + general_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const _NOEXCEPT + { + return (*m_container)[m_itr[n]];; + } + bool operator==(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator !=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(general_array_view_iterator& rhs) _NOEXCEPT + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +} // namespace Guide + +#pragma pop_macro("_NOEXCEPT") diff --git a/include/fail_fast.h b/include/fail_fast.h new file mode 100644 index 0000000..bd93cb7 --- /dev/null +++ b/include/fail_fast.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 + +#include + +namespace Guide +{ + +// +// Having "fail fast" result in an exception makes unit testing +// the GSL classes that rely upon it much simpler. +// +#if defined(SAFER_CPP_TESTING) + +struct fail_fast : public std::exception {}; +inline void fail_fast_assert(bool cond) { if (!cond) throw fail_fast(); } + +#else + +inline void fail_fast_assert(bool cond) { if (!cond) std::terminate(); } + +#endif // SAFER_CPP_TESTING + +} \ No newline at end of file diff --git a/include/gsl.h b/include/gsl.h new file mode 100644 index 0000000..f4c8857 --- /dev/null +++ b/include/gsl.h @@ -0,0 +1,284 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 + +#include "array_view.h" // array_view, strided_array_view... +#include "string_view.h" // zstring, string_view, zstring_builder... +#include + +namespace Guide +{ + +// +// GSL.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +// +// GSL.assert: assertions +// +#define Expects(x) Guide::fail_fast_assert((x)) +#define Ensures(x) Guide::fail_fast_assert((x)) + +// +// GSL.util: utilities +// + +// Final_act allows you to ensure something gets run at the end of a scope +template +class Final_act +{ +public: + explicit Final_act(F f) : f_(f) {} + + Final_act(const Final_act&& other) : f_(other.f_) {} + Final_act(const Final_act&) = delete; + Final_act& operator=(const Final_act&) = delete; + + ~Final_act() { f_(); } + +private: + F f_; +}; + +// finally() - convenience function to generate a Final_act +template +Final_act finally(F f) { return Final_act(f); } + +// narrow_cast(): a searchable way to do narrowing casts of values +template +T narrow_cast(U u) { return static_cast(u); } + +struct narrowing_error : public std::exception {}; +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; } + +template +T& at(std::array& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; } + +template +typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; } + + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from U* fails with nullptr +// - allow implicit conversion to U* +// +template +class not_null +{ +public: + not_null(T t) : ptr_(t) { ensure_invariant(); } + + // deleting these two prevents compilation when initialized with a nullptr or literal 0 + not_null(std::nullptr_t) = delete; + not_null(int) = delete; + + not_null(const not_null &other) = default; + + template ::value>> + not_null(const not_null &other) : ptr_(other.get()) + { + } + + not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } + + // prevents compilation when someone attempts to assign a nullptr + not_null& operator=(std::nullptr_t) = delete; + not_null& operator=(int) = delete; + + T get() const { +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } // the assume() should help the optimizer + + operator T() const { return get(); } + T operator->() const { return get(); } + + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } +private: + T ptr_; + + // 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); } + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+(size_t) = delete; + not_null& operator+=(size_t) = delete; + not_null& operator-(size_t) = delete; + not_null& operator-=(size_t) = delete; +}; + + +// +// maybe_null +// +// Describes an optional pointer - provides symmetry with not_null +// +template +class maybe_null_dbg +{ +public: + maybe_null_dbg() : ptr_(nullptr), tested_(false) {} + + maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} + maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} + + template ::value>> + maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) + { + } + + template ::value>> + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) + { + } + + maybe_null_dbg& operator=(const T& p) + { + if (ptr_ != p) + { + ptr_ = p; + tested_ = false; + } + return *this; + } + + maybe_null_dbg& operator=(const maybe_null_dbg& rhs) + { + if (this != &rhs) + { + ptr_ = rhs.ptr_; + tested_ = false; + } + return *this; + } + + bool present() const { tested_ = true; return ptr_ != nullptr; } + + bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } + + T get() const { + fail_fast_assert(tested_); +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } + + operator T() const { return get(); } + T operator->() const { return get(); } + +private: + const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + maybe_null_dbg& operator++() = delete; + maybe_null_dbg& operator--() = delete; + maybe_null_dbg operator++(int) = delete; + maybe_null_dbg operator--(int) = delete; + maybe_null_dbg& operator+(size_t) = delete; + maybe_null_dbg& operator+=(size_t) = delete; + maybe_null_dbg& operator-(size_t) = delete; + maybe_null_dbg& operator-=(size_t) = delete; + + T ptr_; + mutable bool tested_; +}; + +template +class maybe_null_ret +{ +public: + maybe_null_ret() : ptr_(nullptr) {} + maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} + maybe_null_ret(const T& p) : ptr_(p) {} + maybe_null_ret(const maybe_null_ret& rhs) = default; + + template ::value>> + maybe_null_ret(const not_null &other) : ptr_(other.get()) + { + } + + template ::value>> + maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) + { + } + + template ::value>> + maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) + { + } + + maybe_null_ret& operator=(const T& p) { if (ptr_ != p) { ptr_ = p; } return *this; } + maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; + + bool present() const { return ptr_ != nullptr; } + + T get() const { return ptr_; } + + operator T() const { return get(); } + T operator->() const { return get(); } + +private: + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + maybe_null_ret& operator++() = delete; + maybe_null_ret& operator--() = delete; + maybe_null_ret operator++(int) = delete; + maybe_null_ret operator--(int) = delete; + maybe_null_ret& operator+(size_t) = delete; + maybe_null_ret& operator+=(size_t) = delete; + maybe_null_ret& operator-(size_t) = delete; + maybe_null_ret& operator-=(size_t) = delete; + + const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type + T ptr_; +}; + +template using maybe_null = maybe_null_ret; + +} // namespace Guide diff --git a/include/string_view.h b/include/string_view.h new file mode 100644 index 0000000..7becc8e --- /dev/null +++ b/include/string_view.h @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 + +#include "array_view.h" +#include + +namespace Guide +{ +// +// 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 +using czstring = const char*; + +template +using cwzstring = const wchar_t*; + +template +using zstring = char*; + +template +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 +using basic_string_view = array_view; + +template +using string_view = basic_string_view; + +template +using cstring_view = basic_string_view; + +template +using wstring_view = basic_string_view; + +template +using cwstring_view = basic_string_view; + + +// +// 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 +array_view ensure_sentinel(const T* seq, SizeType max = std::numeric_limits::max()) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + fail_fast_assert(*cur == Sentinel); + return{ seq, 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 +inline basic_string_view ensure_z(T* const & sz, size_t max = std::numeric_limits::max()) +{ + return ensure_sentinel<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 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 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 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 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 +basic_string_view ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], N); } + +template +basic_string_view::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 +std::basic_string::type> to_string(const basic_string_view& view) +{ + return{ view.data(), view.length() }; +} + + +template +class basic_zstring_builder +{ +public: + using string_view_type = basic_string_view; + 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 + 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 Guide::ensure_z(sv_); } + + iterator begin() const { return sv_.begin(); } + iterator end() const { return sv_.end(); } + +private: + string_view_type sv_; +}; + +template +using zstring_builder = basic_zstring_builder; + +template +using wzstring_builder = basic_zstring_builder; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..8f4385a --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,143 @@ +cmake_minimum_required(VERSION 3.2.2) + +project(GSLTests) + +add_subdirectory(unittest-cpp) + +include_directories( + ../include + ./unittest-cpp +) + +add_definitions(-DSAFER_CPP_TESTING) + +if(MSVC14 OR MSVC12) + # has the support we need +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") + elseif(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() +endif() + +if (NOT EXISTS tests/unittest-cpp) + message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") +endif() + +add_executable(array_view_tests + array_view_tests.cpp +) +target_link_libraries(array_view_tests + UnitTest++ +) +install(TARGETS array_view_tests + RUNTIME DESTINATION bin +) +add_test( + NAME array_view_tests + COMMAND array_view_tests +) + +add_executable(string_view_tests + string_view_tests.cpp +) +target_link_libraries(string_view_tests + UnitTest++ +) +install(TARGETS string_view_tests + RUNTIME DESTINATION bin +) +add_test( + NAME string_view_tests + COMMAND string_view_tests +) + +add_executable(at_tests + at_tests.cpp +) +target_link_libraries(at_tests + UnitTest++ +) +install(TARGETS at_tests + RUNTIME DESTINATION bin +) +add_test( + NAME at_tests + COMMAND at_tests +) + +add_executable(bounds_tests + bounds_tests.cpp +) +target_link_libraries(bounds_tests + UnitTest++ +) +install(TARGETS bounds_tests + RUNTIME DESTINATION bin +) +add_test( + NAME bounds_tests + COMMAND bounds_tests +) + +add_executable(maybenull_tests + maybenull_tests.cpp +) +target_link_libraries(maybenull_tests + UnitTest++ +) +install(TARGETS maybenull_tests + RUNTIME DESTINATION bin +) +add_test( + NAME maybenull_tests + COMMAND maybenull_tests +) + +add_executable(notnull_tests + notnull_tests.cpp +) +target_link_libraries(notnull_tests + UnitTest++ +) +install(TARGETS notnull_tests + RUNTIME DESTINATION bin +) +add_test( + NAME notnull_tests + COMMAND notnull_tests +) + +add_executable(assertion_tests + assertion_tests.cpp +) +target_link_libraries(assertion_tests + UnitTest++ +) +install(TARGETS assertion_tests + RUNTIME DESTINATION bin +) +add_test( + NAME assertion_tests + COMMAND assertion_tests +) + +add_executable(utils_tests + utils_tests.cpp +) +target_link_libraries(utils_tests + UnitTest++ +) +install(TARGETS utils_tests + RUNTIME DESTINATION bin +) +add_test( + NAME utils_tests + COMMAND utils_tests +) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp new file mode 100644 index 0000000..a97bbe3 --- /dev/null +++ b/tests/array_view_tests.cpp @@ -0,0 +1,648 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace Guide; + +namespace +{ + void use(int&) {} + struct BaseClass {}; + struct DerivedClass : BaseClass {}; +} + +SUITE(array_view_tests) +{ + TEST(basics) + { + auto ptr = as_array_view(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) + { + CHECK(num == 99); + } + + delete[] ptr.data(); + + + static_bounds bounds{ 3 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av(nullptr, bounds); + av.extent(); + av.extent<2>(); + av[8][4][3]; +#endif + } + + TEST (array_view_convertible) + { +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av1(nullptr, b1); +#endif + + auto f = [&]() { array_view av1(nullptr); }; + CHECK_THROW(f(), fail_fast); + + array_view av1(nullptr); + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + array_view av1 = nullptr; + array_view av2(av1); + array_view av2(av1); +#endif + + array_view avd; +#ifdef CONFIRM_COMPILATION_ERRORS + array_view avb = avd; +#endif + array_view avcd = avd; + } + + TEST(boundary_checks) + { + int arr[10][2]; + auto av = as_array_view(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10,2}]), fail_fast); + } + + void overloaded_func(array_view exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void overloaded_func(array_view exp, char expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void fixed_func(array_view exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + TEST(array_view_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_array_view(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(av.as_array_view(dim<>(4), dim<>(3), dim<>(5)), 34); + + //fixed_func(av, 34); + delete[] data; + } + + + TEST(md_access) + { + unsigned int width = 5, height = 20; + + unsigned int imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = as_array_view(image_ptr, imgSize).as_array_view(dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (unsigned int i = 0; i < height; i++) + { + for (unsigned int j = 0; j < width; j++) + { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(array_view_factory_test) + { + { + int * arr = new int[150]; + + auto av = as_array_view(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + + array stdarr{ 0 }; + auto av2 = as_array_view(stdarr); + overloaded_func(av2.as_array_view(dim<>(1), dim<3>(), dim<5>()), 0); + + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + auto av3 = as_array_view(str); + overloaded_func(av3.as_array_view(dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + int a[3][4][5]; + auto av = as_array_view(a); + const int (*b)[4][5]; + b = a; + auto bv = as_array_view(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_array_view(arr); + + vector vec(3); + auto dv = as_array_view(vec); + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_array_view(std::move(vec)); +#endif + } + } + + TEST (array_view_reshape_test) + { + int a[3][4][5]; + auto av = as_array_view(a); + auto av2 = av.as_array_view(dim<60>()); + auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); + auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); + auto av5 = av4.as_array_view(dim<3>(), dim<4>(), dim<5>()); + auto av6 = av5.as_array_view(dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = av6.as_bytes(); + + auto av8 = av7.as_array_view(); + + CHECK(av8.size() == av6.size()); + for (size_t i = 0; i < av8.size(); i++) + { + CHECK(av8[i] == 1); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + struct Foo {char c[11];}; + auto av9 = av7.as_array_view(); +#endif + } + + + TEST (array_view_section_test) + { + int a[30][4][5]; + + auto av = as_array_view(a); + auto sub = av.section({15, 0, 0}, Guide::index<3>{2, 2, 2}); + auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1}); + } + + + TEST(constructors) + { + array_view av(nullptr); + CHECK(av.length() == 0); + + array_view av2; + CHECK(av2.length() == 0); + + array_view av3(nullptr, 0); + CHECK(av3.length() == 0); + + // Constructing from a nullptr + length is specifically disallowed + auto f = [&]() {array_view av4(nullptr, 2);}; + CHECK_THROW(f(), fail_fast); + + int arr1[2][3]; + array_view av5(arr1); + + array arr2; + array_view av6(arr2); + + vector vec1(19); + array_view av7(vec1); + CHECK(av7.length() == 19); + + + array_view av8; + CHECK(av8.length() == 0); + array_view av9(arr2); + CHECK(av9.length() == 15); + + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av10; + DerivedClass *p = nullptr; + array_view av11(p, 0); +#endif + + + } + + TEST(copyandassignment) + { + array_view av1; + + int arr[] = {3, 4, 5}; + av1 = arr; + array_view, dynamic_range> av2; + av2 = av1; + } + + TEST(array_view_first) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.first<2>().bounds() == static_bounds())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + array_view av = arr; + CHECK((av.first<0>().bounds() == static_bounds())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.first<5>().bounds() == static_bounds())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds()); + CHECK(av.first<6>().length() == 6); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + array_view av; + CHECK((av.first<0>().bounds() == static_bounds())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(array_view_last) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.last<2>().bounds() == static_bounds())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + array_view av = arr; + CHECK((av.last<0>().bounds() == static_bounds())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.last<5>().bounds() == static_bounds())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + array_view av; + CHECK((av.last<0>().bounds() == static_bounds())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(custmized_array_view_size) + { + double (*arr)[3][4] = new double[100][3][4]; + array_view, dynamic_range, 3, 4> av1(arr, (char)10); + + struct EffectiveStructure + { + double* v1; + char v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + array_view av2 = av1.as_array_view(dim<>(5), dim<6>(), dim<4>()); + + } + + TEST(array_view_sub) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.sub<2,2>().bounds() == static_bounds())); + CHECK((av.sub<2,2>().length() == 2)); + CHECK(av.sub(2,2).length() == 2); + } + + + { + array_view av = arr; + CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.sub<0,5>().bounds() == static_bounds())); + CHECK((av.sub<0,5>().length() == 5)); + CHECK(av.sub(0,5).length() == 5); + } + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.sub<5,0>().bounds() == static_bounds())); + CHECK((av.sub<5,0>().length() == 0)); +#endif + CHECK_THROW(av.sub(5,0).length(), fail_fast); + } + + { + array_view av; + CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + } + } + + void AssertNullEmptyProperties(array_view& av) + { + CHECK(av.length() == 0); + CHECK(av.data() == nullptr); + CHECK(!av); + } + + template + void AssertContentsMatch(T a1, U a2) + { + CHECK(a1.length() == a2.length()); + for (size_t i = 0; i < a1.length(); ++i) + CHECK(a1[i] == a2[i]); + } + + TEST(TestNullConstruction) + { + array_view av; + AssertNullEmptyProperties(av); + + array_view av2(nullptr); + AssertNullEmptyProperties(av2); + } + + TEST(ArrayConstruction) + { + int a[] = { 1, 2, 3, 4 }; + + array_view av = { &a[1], 3 }; + CHECK(av.length() == 3); + + array_view av3 = { a, 2 }; + CHECK(av3.length() == 2); + + array_view av2 = a; + CHECK(av2.length() == 4); + } + + TEST(NonConstConstConversions) + { + int a[] = { 1, 2, 3, 4 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view cav = a; + array_view av = cav; +#else + array_view av = a; + array_view cav = av; +#endif + AssertContentsMatch(av, cav); + } + + TEST(FixedSizeConversions) + { + int arr[] = { 1, 2, 3, 4 }; + + // converting to an array_view from an equal size array is ok + array_view av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + array_view av = av4; + } + { + array_view av = arr; + } + + // initialization or assignment to static array_view that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av2 = arr; + } + { + array_view av2 = av4; + } +#endif + + { + array_view av = arr; + array_view av2 = av; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av = arr; + array_view av2 = av.as_array_view(dim<2>(), dim<2>()); + } +#endif + + { + array_view av = arr; + auto f = [&]() {array_view av2 = av.as_array_view(dim<>(2), dim<>(2));}; + CHECK_THROW(f(), fail_fast); + } + + // but doing so explicitly is ok + + // you can convert statically + { + array_view av2 = {arr, 2}; + } + { + array_view av2 = av4.first<1>(); + } + + // ...or dynamically + { + // NB: implicit conversion to array_view from array_view + array_view av2 = av4.first(1); + } + + // initialization or assignment to static array_view that requires size INCREASE is not ok. + int arr2[2] = { 1, 2 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av4 = arr2; + } + { + array_view av2 = arr2; + array_view av4 = av2; + } +#endif + { + auto f = [&]() {array_view av4 = {arr2, 2};}; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + array_view av = arr2; + auto f = [&](){ array_view av2 = av; }; + CHECK_THROW(f(), fail_fast); + } + + TEST(AsWriteableBytes) + { + int a[] = { 1, 2, 3, 4 }; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + array_view av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + array_view av; + auto wav = av.as_writeable_bytes(); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.bytes() == 0); + } + + { + array_view av = a; + auto wav = av.as_writeable_bytes(); + CHECK(wav.data() == (byte*)&a[0]); + CHECK(wav.length() == sizeof(a)); + } + + } + + + TEST(ArrayViewComparison) + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp new file mode 100644 index 0000000..012a043 --- /dev/null +++ b/tests/assertion_tests.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +using namespace Guide; + +SUITE(assertion_tests) +{ + int f(int i) + { + Expects(i > 0 && i < 10); + return i; + } + + TEST(expects) + { + CHECK(f(2) == 2); + CHECK_THROW(f(10), fail_fast); + } + + int g(int i) + { + i++; + Ensures(i > 0 && i < 10); + return i; + } + + TEST(ensures) + { + CHECK(g(2) == 3); + CHECK_THROW(g(9), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp new file mode 100644 index 0000000..6a86307 --- /dev/null +++ b/tests/at_tests.cpp @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include + +using namespace std; +using namespace Guide; + +SUITE(at_tests) +{ + TEST(static_array) + { + int a[] = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(std_array) + { + std::array a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(StdVector) + { + std::vector a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp new file mode 100644 index 0000000..b14a113 --- /dev/null +++ b/tests/bounds_tests.cpp @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include + +using namespace std; +using namespace Guide;; + +namespace +{ + void use(unsigned int&) {} +} + +SUITE(bounds_test) +{ + TEST(basic_bounds) + { + for (auto point : static_bounds { 2 }) + { + for (unsigned int j = 0; j < decltype(point)::rank; j++) + { + use(j); + use(point[j]); + } + } + } + + TEST(bounds_basic) + { + static_bounds b; + auto a = b.slice(); + static_bounds x{ 4 }; + x.slice().slice(); + } + + TEST (arrayview_iterator) + { + static_bounds bounds{ 3 }; + + auto itr = bounds.begin(); + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view< int, 4, dynamic_range, 2> av(nullptr, bounds); + + auto itr2 = av.cbegin(); + + for (auto & v : av) { + v = 4; + } + fill(av.begin(), av.end(), 0); +#endif + } + + TEST (bounds_convertible) + { + static_bounds b1; + static_bounds b2 = b1; + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b4 = b2; +#endif + + static_bounds b3 = b1; + static_bounds b4 = b3; + + static_bounds b11; + + static_bounds b5; + static_bounds b6; + + b5 = static_bounds(); + CHECK_THROW(b6 = b5, fail_fast); + b5 = static_bounds(); + b6 = b5; + + CHECK(b5 == b6); + CHECK(b5.size() == b6.size()); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp new file mode 100644 index 0000000..1fdfb78 --- /dev/null +++ b/tests/maybenull_tests.cpp @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +using namespace Guide; + +struct MyBase { bool foo() { return true; } }; +struct MyDerived : public MyBase {}; +struct Unrelated {}; + +SUITE(MaybeNullTests) +{ + TEST(TestMaybeNull1) + { + int n = 5; + maybe_null_dbg opt_n(&n); + int result = 0; + bool threw = false; + + CHECK_THROW(result = *opt_n, fail_fast); + } + + TEST(TestMaybeNull2) + { + int n = 5; + maybe_null opt_n(&n); + int result = 0; + if (opt_n.present()) + result = *opt_n; + } + + TEST(TestMaybeNull3) + { + int n = 5; + maybe_null opt_n(&n); + int result = 0; + if (opt_n != nullptr) + result = *opt_n; + } + + int test4_helper(maybe_null p) + { + if (p != nullptr) + return *p; + return -1; + } + + TEST(TestMaybeNull4) + { + int n = 5; + int result = 0; + result = test4_helper(&n); + } + + int test5_helper(maybe_null_dbg p) + { + return *p; + } + + TEST(TestMaybeNull5) + { + int n = 5; + int result = 0; + bool threw = false; + + CHECK_THROW(result = test5_helper(&n), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + int TestMaybeNull6() + { + int n; + maybe_null o(n); + } +#endif + + int g_int; + void test7_helper(maybe_null *> outptr) + { + g_int = 5; + + if (outptr.present()) + *outptr = &g_int; + } + + void test7b_helper(maybe_null_dbg *> outptr) + { + g_int = 5; + + if (outptr.present()) + *outptr = &g_int; + } + + TEST(TestMaybeNull7a) + { + maybe_null outval; + test7_helper(&outval); + CHECK(outval.present() && *outval == 5); + } + + TEST(TestMaybeNull7b) + { + maybe_null_dbg outval; + test7b_helper(&outval); + CHECK_THROW((void)*outval, fail_fast); + } + + int test8_helper1(maybe_null_dbg opt) + { + return *opt; + } + + int test8_helper2a(maybe_null_dbg opt) + { + if (!opt.present()) + return 0; + return test8_helper1(opt); + } + + TEST(TestMaybeNull8a) + { + int n = 5; + maybe_null_dbg opt(&n); + CHECK_THROW(test8_helper2a(opt), fail_fast); + } + +#ifdef CONVERT_TO_PTR_TO_CONST + int test9_helper(maybe_null copt) + { + if (copt.present()) + return *copt; + return 0; + } + + void TestMaybeNull9() + { + int n = 5; + maybe_null opt(&n); + CHECK_THROW(test9_helper(opt), fail_fast); + } +#endif + + TEST(TestMaybeNullCasting) + { + MyDerived derived; + maybe_null p = &derived; + CHECK(p.present()); + + maybe_null q = p; + CHECK(q == p); + +#ifdef CONFIRM_COMPILATION_ERRORS + maybe_null r = p; + maybe_null s = reinterpret_cast(p); +#endif + maybe_null_dbg t = reinterpret_cast(p.get()); + + CHECK_THROW((void)(void*)t.get(), fail_fast); + maybe_null_dbg u = reinterpret_cast(p.get()); + CHECK(u.present()); + CHECK((void*)p.get() == (void*)u.get()); + } + + TEST(TestMaybeNullArrow) + { + MyDerived derived; + maybe_null_dbg p = &derived; + + CHECK_THROW(p->foo(), fail_fast); + CHECK(p.present()); + CHECK(p->foo()); + + maybe_null q = p; + CHECK(q.present()); + CHECK(q->foo()); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp new file mode 100644 index 0000000..008cbb3 --- /dev/null +++ b/tests/notnull_tests.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +using namespace Guide; + +struct MyBase {}; +struct MyDerived : public MyBase {}; +struct Unrelated {}; + +// stand-in for a user-defined ref-counted class +template +struct RefCounted +{ + RefCounted(T* p) : p_(p) {} + operator T*() { return p_; } + T* p_; +}; + +SUITE(NotNullTests) +{ + + bool helper(not_null p) + { + return *p == 12; + } + + TEST(TestNotNullConstructors) + { +#ifdef CONFIRM_COMPILATION_ERRORS + not_null p = nullptr; // yay...does not compile! + not_null*> p = 0; // yay...does not compile! + not_null p; // yay...does not compile! + std::unique_ptr up = std::make_unique(120); + not_null p = up; +#endif + int i = 12; + auto rp = RefCounted(&i); + not_null p(rp); + CHECK(p.get() == &i); + } + + TEST(TestNotNullCasting) + { + MyDerived derived; + not_null p = &derived; + not_null q = p; + CHECK(q == p); + +#ifdef CONFIRM_COMPILATION_ERRORS + not_null r = p; + not_null s = reinterpret_cast(p); +#endif + not_null t = reinterpret_cast(p.get()); + CHECK((void*)p.get() == (void*)t.get()); + } + + TEST(TestNotNullAssignment) + { + int i = 12; + not_null p = &i; + CHECK(helper(p)); + + int* q = nullptr; + CHECK_THROW(p = q, fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp new file mode 100644 index 0000000..c382cf0 --- /dev/null +++ b/tests/string_view_tests.cpp @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include +#include + +using namespace std; +using namespace Guide; + +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 vec('h', 5); + 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(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(); +} diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp new file mode 100644 index 0000000..0dc4809 --- /dev/null +++ b/tests/utils_tests.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include + +using namespace Guide; + +SUITE(utils_tests) +{ + void f(int& i) + { + i += 1; + } + + TEST(finally_lambda) + { + int i = 0; + { + auto _ = finally([&]() {f(i);}); + CHECK(i == 0); + } + CHECK(i == 1); + } + + TEST(finally_function_with_bind) + { + int i = 0; + { + auto _ = finally(std::bind(&f, std::ref(i))); + CHECK(i == 0); + } + CHECK(i == 1); + } + + int j = 0; + void g() { j += 1; }; + TEST(finally_function_ptr) + { + j = 0; + { + auto _ = finally(&g); + CHECK(j == 0); + } + CHECK(j == 1); + } + + TEST(narrow_cast) + { + int n = 120; + char c = narrow_cast(n); + CHECK(c == 120); + + n = 300; + unsigned char uc = narrow_cast(n); + CHECK(uc == 44); + } + + TEST(narrow) + { + int n = 120; + char c = narrow(n); + CHECK(c == 120); + + n = 300; + CHECK_THROW(narrow(n), narrowing_error); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} -- cgit v1.2.3 From 1e13a615dab72896d06de066e1a85f995668e6c9 Mon Sep 17 00:00:00 2001 From: Sergiy Oryekhov Date: Fri, 21 Aug 2015 14:18:07 -0700 Subject: Access test. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c0f100e..c3e7a49 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,4 @@ As the types are entirely implemented inline in headers, there are no linking re Just place the contents of the [include](./include) directory within your source tree so it is available to your compiler, then include the appropriate headers in your program, and away you go! + -- cgit v1.2.3 From 87e00114366b5bf066f9280cb6ec39fbb6454d04 Mon Sep 17 00:00:00 2001 From: Sergiy Oryekhov Date: Fri, 21 Aug 2015 15:19:05 -0700 Subject: Access test (2) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c3e7a49..c0f100e 100644 --- a/README.md +++ b/README.md @@ -60,4 +60,3 @@ As the types are entirely implemented inline in headers, there are no linking re Just place the contents of the [include](./include) directory within your source tree so it is available to your compiler, then include the appropriate headers in your program, and away you go! - -- cgit v1.2.3 From 8a38a411cab8abbf75b04c85393202aebcbab2b7 Mon Sep 17 00:00:00 2001 From: Andrew Pardoe Date: Sun, 23 Aug 2015 08:34:32 -0700 Subject: Adding CONTRIBUTING.md and fixing up README.md, LICENSE --- CONTRIBUTING.md | 29 +++++++++++++++++++++++++++++ LICENSE | 10 ++++++++++ README.md | 15 +++++++++------ 3 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3ce68e8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +## Contributing to the Guidelines Support Library + +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines. + +GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue +tracking the suggestion that has been approved the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted +a PR, please post a comment in the associated issue to avoid duplication of effort. + +## Legal +You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us and the community permission to +use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright. + +Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. + +## Housekeeping +Your pull request should: + +* Include a description of what your change intends to do +* Be a child commit of a reasonably recent commit in the **master** branch + * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) +* It is desirable, but not necessary, for the tests to pass at each commit. Please see [README.md](./README.md) for instructions to build the test suite. +* Have clear commit messages + * e.g. "Fix issue", "Add tests for type", etc. +* Include appropriate tests + * Tests should include reasonable permutations of the target fix/change + * Include baseline changes with your change + * All changed code must have 100% code coverage +* To avoid line ending issues, set `autocrlf = input` and `whitespace = cr-at-eol` in your git configuration diff --git a/LICENSE b/LICENSE index 011f2f1..aa58667 100644 --- a/LICENSE +++ b/LICENSE @@ -2,6 +2,16 @@ Copyright (c) 2015 Microsoft Corporation. All rights reserved. This code is licensed under the MIT License (MIT). +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + 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 diff --git a/README.md b/README.md index c0f100e..763d09b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ # GSL: Guidelines Support Library -This library contains functions and types that are suggested for use by the -[C++ Coding Guidelines](https://github.com/Microsoft/CppCodingStandards/). +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](isocpp.org). +This repo contains Microsoft's implementation of GSL, tracking Microsoft's fork of the Guidelines. Microsoft's fork can be found here: +[C++ Core Guidelines](https://github.com/Microsoft/CppCoreGuidelines). -These include types like `array_view<>`, `string_view<>`, `owner<>` and others. +The library includes types like `array_view<>`, `string_view<>`, `owner<>` and others. The entire implementation is provided inline in the headers under the [include](./include) directory. While some types have been broken out into their own headers (e.g. [include/array_view.h](./include/array_view.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. +> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to +other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. # Quick Start ## Supported Platforms @@ -23,8 +26,8 @@ The test suite that exercises GSL has been built and passes successfully on the * Linux using Clang\LLVM 3.6 * Linux using GCC 5.1 -> If you successfully port GSL to another platform, we would love to hear from you. Please consider contributing -any changes that were necessary back to this project to benefit the wider community. +> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider +contributing any changes that were necessary back to this project to benefit the wider community. ## Building the tests To build the tests, you will require the following: -- cgit v1.2.3 From 5e945461c96f59fde8c476d13c68254b0585ad3d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 25 Aug 2015 11:29:49 -0700 Subject: Fixed unittest directory check to look in the source directory --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8f4385a..4e057c2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,7 +26,7 @@ else() endif() endif() -if (NOT EXISTS tests/unittest-cpp) +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") endif() -- cgit v1.2.3 From 9f9fad96207d7dbe3a53afa80d7b5847cd8a0e30 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 27 Aug 2015 18:13:49 -0700 Subject: Implemented comparison operators on array_view. --- include/array_view.h | 46 ++++++++++++++++-- tests/array_view_tests.cpp | 117 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 150 insertions(+), 13 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index ecc3d1e..4692e43 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1420,10 +1420,41 @@ public: } template , std::remove_cv_t>::value>> - _CONSTEXPR auto operator == (const basic_array_view & other) const -> decltype(this->m_pdata == other.m_pdata && this->m_bounds == other.m_bounds) _NOEXCEPT - { - return m_pdata == other.m_pdata && m_bounds == other.m_bounds; - } + _CONSTEXPR bool operator== (const basic_array_view & other) const _NOEXCEPT + { + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin(), other.end())); + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator!= (const basic_array_view & other) const _NOEXCEPT + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator< (const basic_array_view & other) const _NOEXCEPT + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator<= (const basic_array_view & other) const _NOEXCEPT + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator> (const basic_array_view & other) const _NOEXCEPT + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator>= (const basic_array_view & other) const _NOEXCEPT + { + return !(*this < other); + } public: template operator[](origin), strided_bounds {extents, details::make_stride(Base::bounds())}}; } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; + using Base::operator<=; + using Base::operator>; + using Base::operator>=; }; template diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index a97bbe3..10c798e 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -630,15 +630,114 @@ SUITE(array_view_tests) TEST(ArrayViewComparison) { - int arr[10][2]; - auto av1 = as_array_view(arr); - array_view av2 = av1; - - CHECK(av1 == av2); - - array_view, 20> av3 = av1.as_array_view(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + } + + { + auto av1 = nullptr; + auto av2 = nullptr; + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 2, 1 }; // bigger + + array_view av1 = nullptr; + array_view av2 = arr; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; + int arr2[] = { 1, 2 }; + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 1, 2, 3 }; + + array_view av1 = { &arr[0], 2 }; // shorter + array_view av2 = arr; // longer + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; // smaller + int arr2[] = { 2, 1 }; // bigger + + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } } } -- cgit v1.2.3 From 9b40a0a7c877529099b228db8d62df4037f4ef33 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 27 Aug 2015 19:49:27 -0700 Subject: Fixed compile error with GCC/clang. --- include/array_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/array_view.h b/include/array_view.h index 4692e43..483485f 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1634,7 +1634,7 @@ public: { } - _CONSTEXPR array_view(nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + _CONSTEXPR array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) { fail_fast_assert(size == 0); } -- cgit v1.2.3 From 75f31dabe99e9df5b5c450de0fd72f03406b6f77 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 27 Aug 2015 22:15:44 -0700 Subject: Removed Debug build warnings from MSVC STL. --- include/array_view.h | 4 ++-- tests/CMakeLists.txt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 483485f..bce3691 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1422,8 +1422,8 @@ public: template , std::remove_cv_t>::value>> _CONSTEXPR bool operator== (const basic_array_view & other) const _NOEXCEPT { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin(), other.end())); + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); } template , std::remove_cv_t>::value>> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4e057c2..dee4e28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,8 +11,9 @@ include_directories( add_definitions(-DSAFER_CPP_TESTING) -if(MSVC14 OR MSVC12) - # has the support we need +if(MSVC14 OR MSVC12) # has the support we need + # remove unnecessary warnings about unchecked iterators + add_definitions(-D_SCL_SECURE_NO_WARNINGS) else() include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) -- cgit v1.2.3 From 17ed5c3664ff8b20a4d4da91f75ebb1b58653e07 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Sun, 30 Aug 2015 23:30:15 -0700 Subject: Fixed issues in strided_array_views, added tests --- include/array_view.h | 372 ++++++++++----- include/fail_fast.h | 12 +- tests/array_view_tests.cpp | 1098 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 1241 insertions(+), 241 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index bce3691..6af4d8d 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -81,10 +81,14 @@ namespace details using value_type = ValueType; static const unsigned int rank = Rank; _CONSTEXPR coordinate_facade() _NOEXCEPT + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + } + _CONSTEXPR coordinate_facade(const value_type(&values)[rank]) _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); for (unsigned int i = 0; i < rank; ++i) - elems[i] = {}; + elems[i] = values[i]; } _CONSTEXPR coordinate_facade(value_type e0) _NOEXCEPT { @@ -96,7 +100,7 @@ namespace details _CONSTEXPR coordinate_facade(std::initializer_list il) { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - fail_fast_assert(il.size() == rank); + fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); for (unsigned int i = 0; i < rank; ++i) { elems[i] = begin(il)[i]; @@ -119,13 +123,13 @@ namespace details // Preconditions: component_idx < rank _CONSTEXPR reference operator[](unsigned int component_idx) { - fail_fast_assert(component_idx < rank); + fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } // Preconditions: component_idx < rank _CONSTEXPR const_reference operator[](unsigned int component_idx) const { - fail_fast_assert(component_idx < rank); + fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT @@ -230,7 +234,7 @@ namespace details elems[i] /= v; return to_concrete(); } - value_type elems[rank]; + value_type elems[rank] = {}; private: _CONSTEXPR const ConcreteType& to_concrete() const _NOEXCEPT { @@ -266,7 +270,8 @@ class index : private details::coordinate_facade, ValueTy { using Base = details::coordinate_facade, ValueType, Rank>; friend Base; - + template + friend class index; public: using Base::rank; using reference = typename Base::reference; @@ -274,15 +279,18 @@ public: using size_type = typename Base::value_type; using value_type = typename Base::value_type; _CONSTEXPR index() _NOEXCEPT : Base(){} - template > - _CONSTEXPR index(value_type e0) _NOEXCEPT : Base(e0){} - _CONSTEXPR index(std::initializer_list il) : Base(il){} + _CONSTEXPR index(const value_type (&values)[rank]) _NOEXCEPT : Base(values) {} + _CONSTEXPR index(std::initializer_list il) : Base(il) {} _CONSTEXPR index(const index &) = default; template _CONSTEXPR index(const index &other) : Base(other) { + } + _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT + { + return (value_type(&)[rank])other.elems[1]; } using Base::operator[]; @@ -318,10 +326,13 @@ public: _CONSTEXPR index(value_type e0) _NOEXCEPT : value(e0) { } + _CONSTEXPR index(const value_type(&values)[1]) _NOEXCEPT : index(values[0]) + { + } // Preconditions: il.size() == rank _CONSTEXPR index(std::initializer_list il) { - fail_fast_assert(il.size() == rank); + fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); value = begin(il)[0]; } @@ -334,18 +345,21 @@ public: value = static_cast(other.value); } - + _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT + { + return other.elems[1]; + } // Preconditions: component_idx < rank _CONSTEXPR reference operator[](size_type component_idx) _NOEXCEPT { - fail_fast_assert(component_idx == 0); + fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } // Preconditions: component_idx < rank _CONSTEXPR const_reference operator[](size_type component_idx) const _NOEXCEPT { - fail_fast_assert(component_idx == 0); + fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } @@ -645,7 +659,7 @@ namespace details template SizeType linearize(const T & arr) const { - fail_fast_assert(arr[Dim] < CurrentRange); + fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); } @@ -782,7 +796,8 @@ public: _CONSTEXPR static_bounds(std::initializer_list il) : m_ranges(il.begin()) { - fail_fast_assert(MyRanges::DynamicNum == il.size() && m_ranges.totalSize() <= details::SizeTypeTraits::max_value); + fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); + fail_fast_assert(m_ranges.totalSize() <= details::SizeTypeTraits::max_value, "Size of the range is larger than the max element of the size type"); } _CONSTEXPR static_bounds() = default; @@ -807,6 +822,11 @@ public: { return static_cast(m_ranges.totalSize()); } + + _CONSTEXPR size_type total_size() const _NOEXCEPT + { + return static_cast(m_ranges.totalSize()); + } _CONSTEXPR size_type linearize(const index_type & idx) const { @@ -826,6 +846,7 @@ public: template _CONSTEXPR size_type extent() const _NOEXCEPT { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return details::createTypeListIndexer(m_ranges).template get().elementNum(); } @@ -866,12 +887,9 @@ class strided_bounds : private details::coordinate_facade, { using Base = details::coordinate_facade, SizeType, Rank>; friend Base; - _CONSTEXPR void makeRegularStriae() _NOEXCEPT - { - strides[rank - 1] = 1; - for (int i = rank - 2; i >= 0; i--) - strides[i] = strides[i + 1] * Base::elems[i + 1]; - } + template + friend class strided_bounds; + public: using Base::rank; using reference = typename Base::reference; @@ -886,37 +904,40 @@ public: static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - _CONSTEXPR strided_bounds() _NOEXCEPT : Base(), strides() {} _CONSTEXPR strided_bounds(const strided_bounds &) = default; template - _CONSTEXPR strided_bounds(const strided_bounds &other) : Base(other) + _CONSTEXPR strided_bounds(const strided_bounds &other) + : Base(other), m_strides(other.strides) { } - _CONSTEXPR strided_bounds(const index_type &extents, const index_type &stride) - : strides(stride) + _CONSTEXPR strided_bounds(const index_type &extents, const index_type &strides) + : m_strides(strides) { for (unsigned int i = 0; i < rank; i++) Base::elems[i] = extents[i]; } - _CONSTEXPR strided_bounds(std::initializer_list il) - : Base(il) + _CONSTEXPR strided_bounds(const value_type(&values)[rank], index_type strides) + : Base(values), m_strides(std::move(strides)) { -#ifndef NDEBUG - for (const auto& v : il) - { - fail_fast_assert(v >= 0); - } -#endif - makeRegularStriae(); } - index_type strides; - _CONSTEXPR size_type size() const _NOEXCEPT + _CONSTEXPR index_type strides() const _NOEXCEPT + { + return m_strides; + } + _CONSTEXPR size_type total_size() const _NOEXCEPT { size_type ret = 0; for (unsigned int i = 0; i < rank; ++i) - ret += (Base::elems[i] - 1) * strides[i]; + ret += (Base::elems[i] - 1) * m_strides[i]; + return ret + 1; + } + _CONSTEXPR size_type size() const _NOEXCEPT + { + size_type ret = 1; + for (unsigned int i = 0; i < rank; ++i) + ret *= Base::elems[i]; return ret; } _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT @@ -933,32 +954,29 @@ public: size_type ret = 0; for (unsigned int i = 0; i < rank; i++) { - fail_fast_assert(idx[i] < Base::elems[i]); - ret += idx[i] * strides[i]; + fail_fast_assert(idx[i] < Base::elems[i], "index is out of bounds of the array"); + ret += idx[i] * m_strides[i]; } return ret; } + _CONSTEXPR size_type stride() const _NOEXCEPT + { + return m_strides[0]; + } + template 1), typename Ret = std::enable_if_t> _CONSTEXPR sliced_type slice() const { - sliced_type ret; - for (unsigned int i = 1; i < rank; ++i) - { - ret.elems[i - 1] = Base::elems[i]; - ret.strides[i - 1] = strides[i]; - } - return ret; + return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; } template _CONSTEXPR size_type extent() const _NOEXCEPT { + static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); return Base::elems[Dim]; } _CONSTEXPR index_type index_bounds() const _NOEXCEPT { - index_type extents; - for (unsigned int i = 0; i < rank; ++i) - extents[i] = (*this)[i]; - return extents; + return index_type(Base::elems); } const_iterator begin() const _NOEXCEPT { @@ -968,6 +986,8 @@ public: { return const_iterator{ *this, index_bounds() }; } +private: + index_type m_strides; }; template @@ -1296,7 +1316,7 @@ namespace details template _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT { - return bnd.strides; + return bnd.strides(); } // Make a stride vector from bounds, assuming continugous memory. @@ -1358,6 +1378,7 @@ public: template _CONSTEXPR size_type extent() const _NOEXCEPT { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return m_bounds.template extent(); } _CONSTEXPR size_type size() const _NOEXCEPT @@ -1372,11 +1393,13 @@ public: { return m_pdata; } - template > + template 1), typename Ret = std::enable_if_t> _CONSTEXPR Ret operator[](size_type idx) const { + fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); const size_type ridx = idx * m_bounds.stride(); - fail_fast_assert(ridx < m_bounds.size()); + + fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); return Ret {m_pdata + ridx, m_bounds.slice()}; } @@ -1422,39 +1445,39 @@ public: template , std::remove_cv_t>::value>> _CONSTEXPR bool operator== (const basic_array_view & other) const _NOEXCEPT { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); } - template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator!= (const basic_array_view & other) const _NOEXCEPT - { - return !(*this == other); - } + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator!= (const basic_array_view & other) const _NOEXCEPT + { + return !(*this == other); + } - template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator< (const basic_array_view & other) const _NOEXCEPT - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator< (const basic_array_view & other) const _NOEXCEPT + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } - template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator<= (const basic_array_view & other) const _NOEXCEPT - { - return !(other < *this); - } + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator<= (const basic_array_view & other) const _NOEXCEPT + { + return !(other < *this); + } - template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator> (const basic_array_view & other) const _NOEXCEPT - { - return (other < *this); - } + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator> (const basic_array_view & other) const _NOEXCEPT + { + return (other < *this); + } - template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator>= (const basic_array_view & other) const _NOEXCEPT - { - return !(*this < other); - } + template , std::remove_cv_t>::value>> + _CONSTEXPR bool operator>= (const basic_array_view & other) const _NOEXCEPT + { + return !(*this < other); + } public: template friend class array_view; - using Base = basic_array_view::value_type, - static_bounds::size_type, FirstDimension, RestDimensions...>>; + using Base = basic_array_view::value_type, + static_bounds::size_type, FirstDimension, RestDimensions... >>; public: using typename Base::bounds_type; @@ -1622,6 +1645,8 @@ public: using typename Base::pointer; using typename Base::value_type; using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; using Base::rank; public: @@ -1644,28 +1669,28 @@ public: _CONSTEXPR array_view() : Base(nullptr, bounds_type()) { } - + // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) template , - typename Dummy = std::enable_if_t::value - && std::is_convertible::value>> - _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) + typename Dummy = std::enable_if_t::value + && std::is_convertible::value >> + _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{ size }) { } // from n-dimensions static array template , typename Dummy = std::enable_if_t::value - && std::is_convertible::value>> - _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + && std::is_convertible::value >> + _CONSTEXPR array_view(T(&arr)[N]) : Base(arr, typename Helper::bounds_type()) { } // from n-dimensions static array with size template , typename Dummy = std::enable_if_t::value - && std::is_convertible::value >> - _CONSTEXPR array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) + && std::is_convertible::value >> + _CONSTEXPR array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) { fail_fast_assert(size <= N); } @@ -1693,17 +1718,17 @@ public: // from containers. It must has .size() and .data() two function signatures template ::value - && std::is_convertible::value - && std::is_convertible, typename Base::bounds_type>::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> + && std::is_convertible::value + && std::is_convertible, typename Base::bounds_type>::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> > - _CONSTEXPR array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + _CONSTEXPR array_view(Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) { } - + _CONSTEXPR array_view(const array_view &) = default; - + // convertible template ::value_type, static_bounds::size_type, FirstDimension, RestDimensions...>>, @@ -1717,30 +1742,29 @@ public: _CONSTEXPR array_view as_array_view(Dimensions2... dims) { static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); - using BoundsType = typename array_view::bounds_type; + using BoundsType = typename array_view::bounds_type; auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); details::verifyBoundsReshape(this->bounds(), tobounds); - return {this->data(), tobounds}; + return{ this->data(), tobounds }; } // to bytes array template ::value_type>>::value> - _CONSTEXPR auto as_bytes() const _NOEXCEPT -> + _CONSTEXPR auto as_bytes() const _NOEXCEPT -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; + return{ reinterpret_cast(this->data()), this->bytes() }; } template ::value_type>>::value> - _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT -> + _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; + return{ reinterpret_cast(this->data()), this->bytes() }; } - // from bytes array template::value, typename Dummy = std::enable_if_t> _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> @@ -1748,7 +1772,7 @@ public: static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + return{ reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } template::value, typename Dummy = std::enable_if_t> @@ -1757,22 +1781,22 @@ public: static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + return{ reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } - + // section on linear space template _CONSTEXPR array_view first() const _NOEXCEPT { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed - return { this->data(), Count }; + return{ this->data(), Count }; } _CONSTEXPR array_view first(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); - return { this->data(), count }; + return{ this->data(), count }; } template @@ -1780,13 +1804,13 @@ public: { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); - return { this->data() + this->size() - Count, Count }; + return{ this->data() + this->size() - Count, Count }; } _CONSTEXPR array_view last(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); - return { this->data() + this->size() - count, count }; + return{ this->data() + this->size() - count, count }; } template @@ -1794,13 +1818,13 @@ public: { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset < bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset < this->size()) && Offset + Count <= this->size())); - return { this->data() + Offset, Count }; + return{ this->data() + Offset, Count }; } _CONSTEXPR array_view sub(size_type offset, size_type count) const _NOEXCEPT { fail_fast_assert((offset == 0 || offset < this->size()) && offset + count <= this->size()); - return { this->data() + offset, count }; + return{ this->data() + offset, count }; } // size @@ -1824,15 +1848,26 @@ public: // section _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const { - return { &this->operator[](origin), strided_bounds {extents, details::make_stride(Base::bounds())}}; + size_type size = bounds().total_size() - bounds().linearize(origin); + return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + } + _CONSTEXPR reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + template 1), typename Dummy = std::enable_if_t> + _CONSTEXPR array_view operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds() }; } - using Base::operator==; - using Base::operator!=; - using Base::operator<; - using Base::operator<=; - using Base::operator>; - using Base::operator>=; + using Base::operator==; + using Base::operator!=; + using Base::operator<; + using Base::operator<=; + using Base::operator>; + using Base::operator>=; }; template @@ -1889,6 +1924,9 @@ template class strided_array_view : public basic_array_view::value_type, strided_bounds::size_type>> { using Base = basic_array_view::value_type, strided_bounds::size_type>>; + + template + friend class strided_array_view; public: using Base::rank; using typename Base::bounds_type; @@ -1896,18 +1934,102 @@ public: using typename Base::pointer; using typename Base::value_type; using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; + + // from static array of size N + template + strided_array_view(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); + } - strided_array_view (pointer ptr, bounds_type bounds): Base(ptr, std::move(bounds)) + // from raw data + strided_array_view(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) { + fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); } + + // from array view template > - strided_array_view (array_view av, index_type strides): Base(av.data(), bounds_type{av.bounds().index_bounds(), strides}) + strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) { + fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); } - // section + + // convertible + template ::value_type, strided_bounds::size_type>>, + typename OtherBaseType = basic_array_view::value_type, strided_bounds::size_type>>, + typename Dummy = std::enable_if_t::value> + > + _CONSTEXPR strided_array_view(const strided_array_view &av): Base(static_cast::Base &>(av)) // static_cast is required + { + } + + // convert from bytes + template ::value>> + strided_array_view as_strided_array_view() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = sizeof(OtherValueType) / sizeof(value_type); + + size_type size = bounds().total_size() / d; + return{ (OtherValueType*)data(), size, bounds_type{ resize_extent(bounds().index_bounds(), d), resize_stride(bounds().strides(), d)} }; + } + strided_array_view section(index_type origin, index_type extents) const { - return { &this->operator[](origin), bounds_type {extents, details::make_stride(Base::bounds())}}; + size_type size = bounds().total_size() - bounds().linearize(origin); + return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; + } + + _CONSTEXPR reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + + template 1), typename Dummy = std::enable_if_t> + _CONSTEXPR strided_array_view operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; + } + +private: + static index_type resize_extent(const index_type& extent, size_t d) + { + fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + + index_type ret = extent; + ret[rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, size_t d, void *p = 0) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, size_t d) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + + for (int i = rank - 2; i >= 0; --i) + { + fail_fast_assert((strides[i] >= strides[i + 1]) && (strides[i] % strides[i + 1] == 0), "Only strided arrays with regular strides can be resized"); + } + + index_type ret = strides / d; + ret[rank - 1] = 1; + + return ret; } }; @@ -1926,7 +2048,7 @@ private: const ArrayView * m_validator; void validateThis() const { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size()); + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } @@ -2043,7 +2165,7 @@ private: template friend class basic_array_view; const ArrayView * m_container; - typename ArrayView::iterator m_itr; + typename ArrayView::bounds_type::iterator m_itr; general_array_view_iterator(const ArrayView *container, bool isbegin = false) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) { diff --git a/include/fail_fast.h b/include/fail_fast.h index bd93cb7..ff5dbc4 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -27,12 +27,22 @@ namespace Guide // #if defined(SAFER_CPP_TESTING) -struct fail_fast : public std::exception {}; +struct fail_fast : public std::exception +{ + fail_fast() = default; + + explicit fail_fast(char const* const message) : + std::exception(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 message) { if (!cond) std::terminate(); } #endif // SAFER_CPP_TESTING diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 10c798e..6b35ab9 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -100,7 +100,7 @@ SUITE(array_view_tests) // out of bounds CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[{1, 3}] = 3), fail_fast); + CHECK_THROW((av[index<2>{1, 3}] = 3), fail_fast); CHECK_THROW(av[10][2], fail_fast); CHECK_THROW((av[{10,2}]), fail_fast); @@ -265,6 +265,877 @@ SUITE(array_view_tests) auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1}); } + TEST(array_view_section) + { + std::vector data(5 * 10); + std::iota(begin(data), end(data), 0); + const array_view av = as_array_view(data).as_array_view(dim<5>(), dim<10>()); + + strided_array_view 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_array_view 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_array_view_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_array_view 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_array_view 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_array_view 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 array_view constructor + { + int arr[] = { 1, 2 }; + + // From non-cv-qualified source + { + const array_view src{ arr }; + + strided_array_view sav{ src, {2, 1} }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().strides() == index<1>{ 1 }); + CHECK(sav[1] == 2); + + strided_array_view 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); + + strided_array_view 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); + + strided_array_view 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); + } + + // From const-qualified source + { + const array_view src{ arr }; + + strided_array_view 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); + + strided_array_view 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); + } + + // From volatile-qualified source + { + const array_view src{ arr }; + + strided_array_view 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); + + strided_array_view 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); + } + + // From cv-qualified source + { + const array_view src{ arr }; + + strided_array_view 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 array_view av(arr, 2); + array_view av2{ av }; + CHECK(av2[1] == 5); + + static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); + + const strided_array_view src{ arr, {2, 1} }; + strided_array_view sav{ src }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().stride() == 1); + CHECK(sav[1] == 5); + + static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); + } + + // Check copy constructor + { + int arr1[2] = { 3, 4 }; + const strided_array_view src1{ arr1, {2, 1} }; + strided_array_view 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_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; + strided_array_view 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_array_view src{ arr1, {{2}, {1}} }; + strided_array_view sav{ arr2, {{3}, {2}} }; + strided_array_view& 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_array_view src1{ arr1, {2, 1} }; + strided_array_view sav1{ arr1b, {1, 1} }; + strided_array_view& 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_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; + strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; + strided_array_view& 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_array_view_slice) + { + std::vector data(5 * 10); + std::iota(begin(data), end(data), 0); + const array_view src = as_array_view(data).as_array_view(dim<5>(), dim<10>()); + + const strided_array_view sav{ src, {{5, 10}, {10, 1}} }; +#ifdef CONFIRM_COMPILATION_ERRORS + const strided_array_view csav{ {src},{ { 5, 10 },{ 10, 1 } } }; +#endif + const strided_array_view csav{ array_view{ src }, { { 5, 10 },{ 10, 1 } } }; + + strided_array_view sav_sl = sav[2]; + CHECK(sav_sl[0] == 20); + CHECK(sav_sl[9] == 29); + + strided_array_view 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_array_view_column_major) + { + // strided_array_view 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_array_view 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_array_view cm_sl = cm_sav[3]; + + CHECK(cm_sl[0] == 10); + CHECK(cm_sl[1] == 11); + CHECK(cm_sl[2] == 12); + + // Section + strided_array_view 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_array_view_bounds) + { + int arr[] = { 0, 1, 2, 3 }; + array_view 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_array_view sav{ av, {{4}, {}} }; + CHECK(sav[0] == 0); + CHECK(sav[3] == 0); + CHECK_THROW(sav[4], fail_fast); + } + + { + // zero extent + strided_array_view sav{ av,{ {},{1} } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // zero extent and stride + strided_array_view sav{ av,{ {},{} } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // strided array ctor with matching strided bounds + strided_array_view 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_array_view 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_array_view 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_array_view { arr, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_array_view { arr, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_array_view { arr, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_array_view { arr, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_array_view { arr, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from array view + CHECK_THROW((strided_array_view { av, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_array_view { av, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_array_view { av, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_array_view { av, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_array_view { av, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from dynamic arrays + CHECK_THROW((strided_array_view { av.data(), 4, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_array_view { av.data(), 4, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_array_view { av.data(), 4, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_array_view { av.data(), 4, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_array_view { av.data(), 4, { 5, 5 } }), fail_fast); + CHECK_THROW((strided_array_view { av.data(), 2, { 2, 2 } }), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + strided_array_view sav0{ av.data(), { 3, 2 } }; + strided_array_view sav1{ arr, { 1 } }; + strided_array_view sav2{ arr, { 1,1,1 } }; + strided_array_view sav3{ av, { 1 } }; + strided_array_view sav4{ av, { 1,1,1 } }; + strided_array_view sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; + strided_array_view sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; + strided_array_view sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + } +#endif + + { + // stride initializer list size should match the rank of the array + CHECK_THROW((index<1>{ 0,1 }), fail_fast); + CHECK_THROW((strided_array_view{ arr, {1, {1,1}} }), fail_fast); + CHECK_THROW((strided_array_view{ arr, {{1,1 }, {1,1}} }), fail_fast); + + CHECK_THROW((strided_array_view{ av, {1, {1,1}} }), fail_fast); + CHECK_THROW((strided_array_view{ av, {{1,1 }, {1,1}} }), fail_fast); + + CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); + CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); + CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); + } + + } + + TEST(strided_array_view_type_conversion) + { + int arr[] = { 0, 1, 2, 3 }; + array_view av(arr); + + { + strided_array_view sav{ av.data(), av.size(), { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_array_view lsav1 = sav.as_strided_array_view(); +#endif + } + { + strided_array_view sav{ av, { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_array_view lsav1 = sav.as_strided_array_view(); +#endif + } + + array_view bytes = av.as_bytes(); + + // retype strided array with regular strides - from raw data + { + strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; + strided_array_view sav2{ bytes.data(), bytes.size(), bounds }; + strided_array_view sav3 = sav2.as_strided_array_view(); + 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 array_view + { + strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; + array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); + strided_array_view sav2{ bytes2, bounds }; + strided_array_view sav3 = sav2.as_strided_array_view(); + 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 } }; + array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); + strided_array_view sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_array_view(), fail_fast); + } + + // retype strided array with not enough elements - strides are too small + { + strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; + array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); + strided_array_view sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_array_view(), 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 } }; + array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); + strided_array_view sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_array_view(), 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 } }; + array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); + strided_array_view sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_array_view(), fail_fast); + } + + // retype strided array with irregular strides - from raw data + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_array_view sav2{ bytes.data(), bytes.size(), bounds }; + CHECK_THROW(sav2.as_strided_array_view(), fail_fast); + } + + // retype strided array with irregular strides - from array_view + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_array_view sav2{ bytes, bounds }; + CHECK_THROW(sav2.as_strided_array_view(), fail_fast); + } + } + + TEST(empty_arrays) + { +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view empty; + strided_array_view empty2; + strided_array_view empty3{ nullptr,{ 0, 1 } }; + } +#endif + + { + array_view empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + + for (auto& v : empty_av) + { + CHECK(false); + } + } + + { + array_view empty_av = {}; + + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + + for (auto& v : empty_av) + { + CHECK(false); + } + } + + { + array_view empty_av(nullptr); + strided_array_view 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) + { + CHECK(false); + } + } + + { + strided_array_view 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) + { + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) + { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + array_view av(arr, 8); + + size_t a[1] = { 0 }; + index<1> i = index<1>(a); + + CHECK(av[i] == 4); + + auto av2 = av.as_array_view(dim<4>(), dim<>(2)); + size_t a2[2] = { 0, 1 }; + index<2> i2 = index<2>(a2); + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_operations) + { + size_t a[3] = { 0, 1, 2 }; + size_t b[3] = { 3, 4, 5 }; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = index<2>::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + + } + + void iterate_second_column(array_view av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({ 0,1 }, { length,1 }); + + CHECK(section.size() == length); + for (unsigned int i = 0; i < section.size(); ++i) + { + CHECK(section[i][0] == av[i][1]); + } + + for (unsigned int i = 0; i < section.size(); ++i) + { + CHECK(section[index<2>({ i,0 })] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (unsigned int i = 0; i < section.bounds().index_bounds()[0]; ++i) + { + for (unsigned int j = 0; j < section.bounds().index_bounds()[1]; ++j) + { + CHECK(section[index<2>({ i,j })] == av[i][1]); + } + } + + unsigned int idx = 0; + for (auto num : section) + { + CHECK(num == av[idx][1]); + idx++; + } + } + + TEST(array_view_section_iteration) + { + int arr[4][2] = { { 4,0 },{ 5,1 },{ 6,2 },{ 7,3 } }; + + // static bounds + { + array_view av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + array_view av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + array_view av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + array_view av(arr, 4); + iterate_second_column(av); + } + } + + TEST(dynamic_array_view_section_iteration) + { + unsigned int height = 4, width = 2; + unsigned int size = height * width; + + auto arr = new int[size]; + for (int unsigned i = 0; i < size; ++i) + { + arr[i] = i; + } + + auto av = as_array_view(arr, size); + + // first bound is dynamic + { + array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + void iterate_every_other_element(array_view av) + { + // pick every other element + + auto length = av.size() / 2; + auto bounds = strided_bounds<1>({ length }, { 2 }); + strided_array_view strided(&av.data()[1], av.size() - 1, bounds); + + CHECK(strided.size() == length); + CHECK(strided.bounds().index_bounds()[0] == length); + for (unsigned int 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_array_view_section_iteration) + { + int arr[8] = {4,0,5,1,6,2,7,3}; + + // static bounds + { + array_view av(arr, 8); + iterate_every_other_element(av); + } + + // dynamic bounds + { + array_view av(arr, 8); + iterate_every_other_element(av); + } + } + + TEST(dynamic_strided_array_view_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_array_view(arr, 8); + iterate_every_other_element(av); + + delete[] arr; + } + + void iterate_second_slice(array_view av) + { + int expected[6] = { 2,3,10,11,18,19 }; + auto section = av.section({ 0,1,0 }, { 3,1,2 }); + + for (unsigned int i = 0; i < section.extent<0>(); ++i) + { + for (unsigned int j = 0; j < section.extent<1>(); ++j) + for (unsigned int k = 0; k < section.extent<2>(); ++k) + CHECK(section[index<3>({ i,j,k })] == expected[2 * i + 2 * j + k]); + } + + for (unsigned int i = 0; i < section.extent<0>(); ++i) + { + for (unsigned int j = 0; j < section.extent<1>(); ++j) + for (unsigned int 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_array_view_section_iteration_3d) + { + int arr[3][4][2]; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 4; ++j) + for (unsigned int k = 0; k < 2; ++k) + arr[i][j][k] = 8 * i + 2 * j + k; + } + + { + array_view av = arr; + iterate_second_slice(av); + } + } + + TEST(dynamic_strided_array_view_section_iteration_3d) + { + unsigned int height = 12, width = 2; + unsigned int size = height * width; + + auto arr = new int[size]; + for (int unsigned i = 0; i < size; ++i) + { + arr[i] = i; + } + + { + auto av = as_array_view(arr, 24).as_array_view(dim<3>(),dim<4>(),dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_array_view(arr, 24).as_array_view(dim<>(3), dim<4>(), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_array_view(arr, 24).as_array_view(dim<3>(), dim<>(4), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_array_view(arr, 24).as_array_view(dim<3>(), dim<4>(), dim<>(2)); + iterate_second_slice(av); + } + delete[] arr; + } + + TEST(strided_array_view_conversion) + { + // get an array_view 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 } }; + + auto s = sizeof(int) / sizeof(byte); + auto d2 = 3 * s; + auto d1 = sizeof(int) * 12 / d2; + + // convert to 4x12 array of bytes + auto av = as_array_view(arr, 4).as_bytes().as_array_view(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_array_view(); // { { 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_array_view transposed{ cs.data(), cs.bounds().total_size(), reverse_bounds }; + + // slice to get a one-dimensional array of c's + strided_array_view 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++; + } + + } TEST(constructors) { @@ -303,8 +1174,6 @@ SUITE(array_view_tests) DerivedClass *p = nullptr; array_view av11(p, 0); #endif - - } TEST(copyandassignment) @@ -510,13 +1379,13 @@ SUITE(array_view_tests) array_view av = cav; #else array_view av = a; - array_view cav = av; + array_view cav = av; #endif AssertContentsMatch(av, cav); } TEST(FixedSizeConversions) - { + { int arr[] = { 1, 2, 3, 4 }; // converting to an array_view from an equal size array is ok @@ -598,7 +1467,7 @@ SUITE(array_view_tests) CHECK_THROW(f(), fail_fast); } - TEST(AsWriteableBytes) + TEST(AsWriteableBytes) { int a[] = { 1, 2, 3, 4 }; @@ -627,117 +1496,116 @@ SUITE(array_view_tests) } - TEST(ArrayViewComparison) { - { - int arr[10][2]; - auto av1 = as_array_view(arr); - array_view av2 = av1; - - CHECK(av1 == av2); - - array_view, 20> av3 = av1.as_array_view(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - } - - { - auto av1 = nullptr; - auto av2 = nullptr; - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 2, 1 }; // bigger - - array_view av1 = nullptr; - array_view av2 = arr; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; - int arr2[] = { 1, 2 }; - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 1, 2, 3 }; - - array_view av1 = { &arr[0], 2 }; // shorter - array_view av2 = arr; // longer - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; // smaller - int arr2[] = { 2, 1 }; // bigger - - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + } + + { + auto av1 = nullptr; + auto av2 = nullptr; + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 2, 1 }; // bigger + + array_view av1 = nullptr; + array_view av2 = arr; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; + int arr2[] = { 1, 2 }; + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 1, 2, 3 }; + + array_view av1 = { &arr[0], 2 }; // shorter + array_view av2 = arr; // longer + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; // smaller + int arr2[] = { 2, 1 }; // bigger + + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } } } -- cgit v1.2.3 From ef6cc65053c804738840007cba2f0448ae52656f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 21:26:17 +0000 Subject: Fixed compilation issues with Clang and GCC on Linux. --- include/array_view.h | 33 ++--- include/fail_fast.h | 11 +- tests/array_view_tests.cpp | 302 ++++++++++++++++++++------------------------- 3 files changed, 155 insertions(+), 191 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 6af4d8d..097d20a 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -290,7 +290,8 @@ public: } _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT { - return (value_type(&)[rank])other.elems[1]; + value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); + return index(arr); } using Base::operator[]; @@ -1647,7 +1648,8 @@ public: using typename Base::index_type; using typename Base::iterator; using typename Base::const_iterator; - using Base::rank; + using typename Base::reference; + using Base::rank; public: // basic @@ -1848,14 +1850,16 @@ public: // section _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const { - size_type size = bounds().total_size() - bounds().linearize(origin); + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; } - _CONSTEXPR reference operator[](const index_type& idx) const + + _CONSTEXPR reference operator[](const index_type& idx) const { return Base::operator[](idx); } - template 1), typename Dummy = std::enable_if_t> + + template 1), typename Dummy = std::enable_if_t> _CONSTEXPR array_view operator[](size_type idx) const { auto ret = Base::operator[](idx); @@ -1936,6 +1940,7 @@ public: using typename Base::index_type; using typename Base::iterator; using typename Base::const_iterator; + using typename Base::reference; // from static array of size N template @@ -1968,26 +1973,26 @@ public: } // convert from bytes - template ::value>> - strided_array_view as_strided_array_view() const + template + strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const { static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); auto d = sizeof(OtherValueType) / sizeof(value_type); - size_type size = bounds().total_size() / d; - return{ (OtherValueType*)data(), size, bounds_type{ resize_extent(bounds().index_bounds(), d), resize_stride(bounds().strides(), d)} }; + size_type size = this->bounds().total_size() / d; + return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; } strided_array_view section(index_type origin, index_type extents) const { - size_type size = bounds().total_size() - bounds().linearize(origin); + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; } _CONSTEXPR reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } + { + return Base::operator[](idx); + } template 1), typename Dummy = std::enable_if_t> _CONSTEXPR strided_array_view operator[](size_type idx) const @@ -2048,7 +2053,7 @@ private: const ArrayView * m_validator; void validateThis() const { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size()); } contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } diff --git a/include/fail_fast.h b/include/fail_fast.h index ff5dbc4..a7385ca 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -27,13 +27,10 @@ namespace Guide // #if defined(SAFER_CPP_TESTING) -struct fail_fast : public std::exception +struct fail_fast : public std::runtime_error { - fail_fast() = default; - - explicit fail_fast(char const* const message) : - std::exception(message) - {} + 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(); } @@ -46,4 +43,4 @@ inline void fail_fast_assert(bool cond, const char* const message) { if (!cond) #endif // SAFER_CPP_TESTING -} \ No newline at end of file +} diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 6b35ab9..60f26d4 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -100,7 +100,7 @@ SUITE(array_view_tests) // out of bounds CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[index<2>{1, 3}] = 3), fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); CHECK_THROW(av[10][2], fail_fast); CHECK_THROW((av[{10,2}]), fail_fast); @@ -621,14 +621,18 @@ SUITE(array_view_tests) // stride initializer list size should match the rank of the array CHECK_THROW((index<1>{ 0,1 }), fail_fast); CHECK_THROW((strided_array_view{ arr, {1, {1,1}} }), fail_fast); +#ifdef _MSC_VER CHECK_THROW((strided_array_view{ arr, {{1,1 }, {1,1}} }), fail_fast); - +#endif CHECK_THROW((strided_array_view{ av, {1, {1,1}} }), fail_fast); +#ifdef _MSC_VER CHECK_THROW((strided_array_view{ av, {{1,1 }, {1,1}} }), fail_fast); - +#endif CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); +#ifdef _MSC_VER CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); +#endif } } @@ -726,69 +730,27 @@ SUITE(array_view_tests) TEST(empty_arrays) { #ifdef CONFIRM_COMPILATION_ERRORS - { - array_view empty; - strided_array_view empty2; - strided_array_view empty3{ nullptr,{ 0, 1 } }; - } + { + array_view empty; + strided_array_view empty2; + strided_array_view empty3{ nullptr,{ 0, 1 } }; + } #endif - { - array_view empty_av(nullptr); - - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - - for (auto& v : empty_av) - { - CHECK(false); - } - } - - { - array_view empty_av = {}; - - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - - for (auto& v : empty_av) - { - CHECK(false); - } - } - - { - array_view empty_av(nullptr); - strided_array_view empty_sav{ empty_av, { 0, 1 } }; + array_view empty_av(nullptr); - 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); + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); - for (auto& v : empty_sav) - { - CHECK(false); - } - } + strided_array_view empty_sav{ empty_av, { 0, 1 } }; - { - strided_array_view empty_sav{ nullptr, 0, { 0, 1 } }; + CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_sav[0], fail_fast); - 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); + strided_array_view empty_sav2{ nullptr, 0, { 0, 1 } }; - for (auto& v : empty_sav) - { - CHECK(false); - } - } + CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_sav[0], fail_fast); } TEST(index_constructor) @@ -803,13 +765,13 @@ SUITE(array_view_tests) array_view av(arr, 8); size_t a[1] = { 0 }; - index<1> i = index<1>(a); + index<1> i = a; CHECK(av[i] == 4); auto av2 = av.as_array_view(dim<4>(), dim<>(2)); size_t a2[2] = { 0, 1 }; - index<2> i2 = index<2>(a2); + index<2> i2 = a2; CHECK(av2[i2] == 0); CHECK(av2[0][i] == 4); @@ -890,7 +852,7 @@ SUITE(array_view_tests) } } - unsigned int idx = 0; + size_t idx = 0; for (auto num : section) { CHECK(num == av[idx][1]); @@ -1498,114 +1460,114 @@ SUITE(array_view_tests) TEST(ArrayViewComparison) { - { - int arr[10][2]; - auto av1 = as_array_view(arr); - array_view av2 = av1; - - CHECK(av1 == av2); - - array_view, 20> av3 = av1.as_array_view(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - } - - { - auto av1 = nullptr; - auto av2 = nullptr; - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 2, 1 }; // bigger - - array_view av1 = nullptr; - array_view av2 = arr; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; - int arr2[] = { 1, 2 }; - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 1, 2, 3 }; - - array_view av1 = { &arr[0], 2 }; // shorter - array_view av2 = arr; // longer - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; // smaller - int arr2[] = { 2, 1 }; // bigger - - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + } + + { + auto av1 = nullptr; + auto av2 = nullptr; + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 2, 1 }; // bigger + + array_view av1 = nullptr; + array_view av2 = arr; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; + int arr2[] = { 1, 2 }; + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 1, 2, 3 }; + + array_view av1 = { &arr[0], 2 }; // shorter + array_view av2 = arr; // longer + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; // smaller + int arr2[] = { 2, 1 }; // bigger + + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } } } -- cgit v1.2.3 From 9a297120227979ad8fa53525178f8edf806634de Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 15:11:07 -0700 Subject: Ensuring compilation works for VS 2013. --- include/array_view.h | 10 ++++++++++ tests/array_view_tests.cpp | 38 ++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 097d20a..9c58ab2 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -52,6 +52,11 @@ #endif // _NOEXCEPT +#if _MSC_VER <= 1800 +#pragma warning(push) +#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#endif // _MSC_VER <= 1800 + namespace Guide { /* @@ -2275,4 +2280,9 @@ general_array_view_iterator operator+(typename general_array_view_ite } // namespace Guide +#if _MSC_VER <= 1800 +#pragma warning(pop) +#endif // _MSC_VER <= 1800 + + #pragma pop_macro("_NOEXCEPT") diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 60f26d4..ec1d31d 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -320,17 +320,29 @@ SUITE(array_view_tests) CHECK(sav.bounds().strides() == index<1>{ 1 }); CHECK(sav[1] == 2); - strided_array_view sav_c{ {src}, {2, 1} }; +#if _MSC_VER > 1800 + strided_array_view sav_c{ array_view{src}, strided_bounds<1>{2, 1} }; +#else + strided_array_view sav_c{ array_view{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_array_view sav_v{ {src}, {2, 1} }; +#else + strided_array_view sav_v{ array_view{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_array_view sav_cv{ {src}, {2, 1} }; +#else + strided_array_view sav_cv{ array_view{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); @@ -345,7 +357,12 @@ SUITE(array_view_tests) CHECK(sav_c.bounds().strides() == index<1>{ 1 }); CHECK(sav_c[1] == 2); +#if _MSC_VER > 1800 strided_array_view sav_cv{ {src}, {2, 1} }; +#else + strided_array_view sav_cv{ array_view{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); @@ -360,7 +377,11 @@ SUITE(array_view_tests) CHECK(sav_v.bounds().strides() == index<1>{ 1 }); CHECK(sav_v[1] == 2); +#if _MSC_VER > 1800 strided_array_view sav_cv{ {src}, {2, 1} }; +#else + strided_array_view sav_cv{ array_view{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); @@ -839,7 +860,8 @@ SUITE(array_view_tests) for (unsigned int i = 0; i < section.size(); ++i) { - CHECK(section[index<2>({ i,0 })] == av[i][1]); + auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); } CHECK(section.bounds().index_bounds()[0] == length); @@ -848,7 +870,8 @@ SUITE(array_view_tests) { for (unsigned int j = 0; j < section.bounds().index_bounds()[1]; ++j) { - CHECK(section[index<2>({ i,j })] == av[i][1]); + auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); } } @@ -923,7 +946,11 @@ SUITE(array_view_tests) // 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_array_view strided(&av.data()[1], av.size() - 1, bounds); CHECK(strided.size() == length); @@ -982,7 +1009,10 @@ SUITE(array_view_tests) { for (unsigned int j = 0; j < section.extent<1>(); ++j) for (unsigned int k = 0; k < section.extent<2>(); ++k) - CHECK(section[index<3>({ i,j,k })] == expected[2 * i + 2 * j + k]); + { + auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro + CHECK(section[idx] == expected[2 * i + 2 * j + k]); + } } for (unsigned int i = 0; i < section.extent<0>(); ++i) -- cgit v1.2.3 From 383dc507a4b9385d24218fed70a4fd0be74df4a0 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 15:41:40 -0700 Subject: Fixes for review feedback. --- include/array_view.h | 4 ++-- tests/array_view_tests.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 9c58ab2..ddf6677 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1654,7 +1654,7 @@ public: using typename Base::iterator; using typename Base::const_iterator; using typename Base::reference; - using Base::rank; + using Base::rank; public: // basic @@ -2058,7 +2058,7 @@ private: const ArrayView * m_validator; void validateThis() const { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size()); + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index ec1d31d..12344d9 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -321,9 +321,9 @@ SUITE(array_view_tests) CHECK(sav[1] == 2); #if _MSC_VER > 1800 - strided_array_view sav_c{ array_view{src}, strided_bounds<1>{2, 1} }; + strided_array_view sav_c{ {src}, {2, 1} }; #else - strided_array_view sav_c{ array_view{src}, strided_bounds<1>{2, 1} }; + strided_array_view sav_c{ array_view{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_c.bounds().strides() == index<1>{ 1 }); @@ -341,7 +341,7 @@ SUITE(array_view_tests) #if _MSC_VER > 1800 strided_array_view sav_cv{ {src}, {2, 1} }; #else - strided_array_view sav_cv{ array_view{src}, strided_bounds<1>{2, 1} }; + strided_array_view sav_cv{ array_view{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); -- cgit v1.2.3 From 18cd9801b515151e09e1c3eb203ef294f0ff8421 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 14 Sep 2015 16:34:26 -0700 Subject: Reverted formatting changes --- include/array_view.h | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 6af4d8d..ff3b1cf 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1637,7 +1637,7 @@ class array_view : public basic_array_view friend class array_view; using Base = basic_array_view::value_type, - static_bounds::size_type, FirstDimension, RestDimensions... >>; + static_bounds::size_type, FirstDimension, RestDimensions...>>; public: using typename Base::bounds_type; @@ -1672,17 +1672,17 @@ public: // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) template , - typename Dummy = std::enable_if_t::value - && std::is_convertible::value >> - _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{ size }) + typename Dummy = std::enable_if_t::value + && std::is_convertible::value>> + _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) { } // from n-dimensions static array template , typename Dummy = std::enable_if_t::value - && std::is_convertible::value >> - _CONSTEXPR array_view(T(&arr)[N]) : Base(arr, typename Helper::bounds_type()) + && std::is_convertible::value>> + _CONSTEXPR array_view(T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) { } @@ -1718,11 +1718,11 @@ public: // from containers. It must has .size() and .data() two function signatures template ::value - && std::is_convertible::value + && std::is_convertible::value && std::is_convertible, typename Base::bounds_type>::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > - _CONSTEXPR array_view(Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + _CONSTEXPR array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) { } @@ -1745,7 +1745,7 @@ public: using BoundsType = typename array_view::bounds_type; auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); details::verifyBoundsReshape(this->bounds(), tobounds); - return{ this->data(), tobounds }; + return {this->data(), tobounds}; } // to bytes array @@ -1754,7 +1754,7 @@ public: array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return{ reinterpret_cast(this->data()), this->bytes() }; + return { reinterpret_cast(this->data()), this->bytes() }; } template ::value_type>>::value> @@ -1762,9 +1762,10 @@ public: array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return{ reinterpret_cast(this->data()), this->bytes() }; + return { reinterpret_cast(this->data()), this->bytes() }; } + // from bytes array template::value, typename Dummy = std::enable_if_t> _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> @@ -1772,7 +1773,7 @@ public: static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return{ reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } template::value, typename Dummy = std::enable_if_t> @@ -1781,7 +1782,7 @@ public: static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return{ reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; } // section on linear space @@ -1790,13 +1791,13 @@ public: { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed - return{ this->data(), Count }; + return { this->data(), Count }; } _CONSTEXPR array_view first(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); - return{ this->data(), count }; + return { this->data(), count }; } template @@ -1804,13 +1805,13 @@ public: { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); - return{ this->data() + this->size() - Count, Count }; + return { this->data() + this->size() - Count, Count }; } _CONSTEXPR array_view last(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); - return{ this->data() + this->size() - count, count }; + return { this->data() + this->size() - count, count }; } template @@ -1818,13 +1819,13 @@ public: { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset < bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset < this->size()) && Offset + Count <= this->size())); - return{ this->data() + Offset, Count }; + return { this->data() + Offset, Count }; } _CONSTEXPR array_view sub(size_type offset, size_type count) const _NOEXCEPT { fail_fast_assert((offset == 0 || offset < this->size()) && offset + count <= this->size()); - return{ this->data() + offset, count }; + return { this->data() + offset, count }; } // size @@ -1849,7 +1850,7 @@ public: _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const { size_type size = bounds().total_size() - bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + return { &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; } _CONSTEXPR reference operator[](const index_type& idx) const { -- cgit v1.2.3 From e5b79d242c222e7f4c1b34456b22f3f45c08d0fb Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 14 Sep 2015 16:38:25 -0700 Subject: Reverted formatting changes --- include/array_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/array_view.h b/include/array_view.h index ff3b1cf..419397d 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1682,7 +1682,7 @@ public: template , typename Dummy = std::enable_if_t::value && std::is_convertible::value>> - _CONSTEXPR array_view(T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) { } -- cgit v1.2.3 From c6a2f8c086aaa2f344c3d14504ef4b1cde310254 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 17:51:31 -0700 Subject: Testing tab fixup. --- tests/array_view_tests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 12344d9..4537ce8 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -751,11 +751,11 @@ SUITE(array_view_tests) TEST(empty_arrays) { #ifdef CONFIRM_COMPILATION_ERRORS - { - array_view empty; - strided_array_view empty2; - strided_array_view empty3{ nullptr,{ 0, 1 } }; - } + { + array_view empty; + strided_array_view empty2; + strided_array_view empty3{ nullptr,{ 0, 1 } }; + } #endif array_view empty_av(nullptr); -- cgit v1.2.3 From 3ff9b17a9f82225c99d9312ccc0227b8f8b594e6 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 17:57:29 -0700 Subject: Restoring empty_array_view tests. --- tests/array_view_tests.cpp | 60 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 4537ce8..63909fa 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -758,20 +758,62 @@ SUITE(array_view_tests) } #endif - array_view empty_av(nullptr); + { + array_view empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + + for (auto& v : empty_av) + { + CHECK(false); + } + } + + { + array_view empty_av = {}; + + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + + for (auto& v : empty_av) + { + CHECK(false); + } + } - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); + { + array_view empty_av(nullptr); + strided_array_view empty_sav{ empty_av, { 0, 1 } }; - strided_array_view 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); - CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_sav[0], fail_fast); + for (auto& v : empty_sav) + { + CHECK(false); + } + } - strided_array_view empty_sav2{ nullptr, 0, { 0, 1 } }; + { + strided_array_view 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); - CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_sav[0], fail_fast); + for (auto& v : empty_sav) + { + CHECK(false); + } + } } TEST(index_constructor) -- cgit v1.2.3 From 54ec02fef6070c55a8fbbfbc82a161d0060b3114 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 14 Sep 2015 18:04:40 -0700 Subject: Fixing misformatting of ArrayViewComparison. --- tests/array_view_tests.cpp | 221 ++++++++++++++++++++++----------------------- 1 file changed, 109 insertions(+), 112 deletions(-) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 63909fa..0525f9d 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -765,7 +765,6 @@ SUITE(array_view_tests) CHECK_THROW(empty_av[0], fail_fast); CHECK_THROW(empty_av.begin()[0], fail_fast); CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) { CHECK(false); @@ -774,12 +773,10 @@ SUITE(array_view_tests) { array_view empty_av = {}; - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); CHECK_THROW(empty_av[0], fail_fast); CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - + CHECK_THROW(empty_av.cbegin()[0], fail_fast); for (auto& v : empty_av) { CHECK(false); @@ -1532,114 +1529,114 @@ SUITE(array_view_tests) TEST(ArrayViewComparison) { - { - int arr[10][2]; - auto av1 = as_array_view(arr); - array_view av2 = av1; - - CHECK(av1 == av2); - - array_view, 20> av3 = av1.as_array_view(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - } - - { - auto av1 = nullptr; - auto av2 = nullptr; - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 2, 1 }; // bigger - - array_view av1 = nullptr; - array_view av2 = arr; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; - int arr2[] = { 1, 2 }; - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 1, 2, 3 }; - - array_view av1 = { &arr[0], 2 }; // shorter - array_view av2 = arr; // longer - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; // smaller - int arr2[] = { 2, 1 }; // bigger - - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + } + + { + auto av1 = nullptr; + auto av2 = nullptr; + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 2, 1 }; // bigger + + array_view av1 = nullptr; + array_view av2 = arr; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; + int arr2[] = { 1, 2 }; + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 1, 2, 3 }; + + array_view av1 = { &arr[0], 2 }; // shorter + array_view av2 = arr; // longer + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; // smaller + int arr2[] = { 2, 1 }; // bigger + + array_view av1 = arr1; + array_view av2 = arr2; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } } } -- cgit v1.2.3 From 1a864987339f170f7ed35a362702f0d5fabac2a7 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 14 Sep 2015 18:55:06 -0700 Subject: Formatting --- include/array_view.h | 20 ++++++++++---------- tests/array_view_tests.cpp | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 2ff693e..ec3edfd 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -295,8 +295,8 @@ public: } _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT { - value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); - return index(arr); + value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); + return index(arr); } using Base::operator[]; @@ -1860,12 +1860,12 @@ public: return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; } - _CONSTEXPR reference operator[](const index_type& idx) const + _CONSTEXPR reference operator[](const index_type& idx) const { return Base::operator[](idx); } - template 1), typename Dummy = std::enable_if_t> + template 1), typename Dummy = std::enable_if_t> _CONSTEXPR array_view operator[](size_type idx) const { auto ret = Base::operator[](idx); @@ -1946,7 +1946,7 @@ public: using typename Base::index_type; using typename Base::iterator; using typename Base::const_iterator; - using typename Base::reference; + using typename Base::reference; // from static array of size N template @@ -1979,8 +1979,8 @@ public: } // convert from bytes - template - strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const + template + strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const { static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); auto d = sizeof(OtherValueType) / sizeof(value_type); @@ -1996,9 +1996,9 @@ public: } _CONSTEXPR reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } + { + return Base::operator[](idx); + } template 1), typename Dummy = std::enable_if_t> _CONSTEXPR strided_array_view operator[](size_type idx) const diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 0525f9d..5c64119 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -362,7 +362,7 @@ SUITE(array_view_tests) #else strided_array_view sav_cv{ array_view{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); @@ -899,7 +899,7 @@ SUITE(array_view_tests) for (unsigned int i = 0; i < section.size(); ++i) { - auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro + auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro CHECK(section[idx] == av[i][1]); } @@ -909,7 +909,7 @@ SUITE(array_view_tests) { for (unsigned int j = 0; j < section.bounds().index_bounds()[1]; ++j) { - auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro + auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro CHECK(section[idx] == av[i][1]); } } @@ -1048,10 +1048,10 @@ SUITE(array_view_tests) { for (unsigned int j = 0; j < section.extent<1>(); ++j) for (unsigned int k = 0; k < section.extent<2>(); ++k) - { - auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro + { + auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro CHECK(section[idx] == expected[2 * i + 2 * j + k]); - } + } } for (unsigned int i = 0; i < section.extent<0>(); ++i) -- cgit v1.2.3 From 9dac1787688dd18eb4579d8fbf7f36313dd13f3c Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 14 Sep 2015 19:08:03 -0700 Subject: Formatting --- include/array_view.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index ec3edfd..80656a4 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1946,7 +1946,7 @@ public: using typename Base::index_type; using typename Base::iterator; using typename Base::const_iterator; - using typename Base::reference; + using typename Base::reference; // from static array of size N template @@ -1996,9 +1996,9 @@ public: } _CONSTEXPR reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } + { + return Base::operator[](idx); + } template 1), typename Dummy = std::enable_if_t> _CONSTEXPR strided_array_view operator[](size_type idx) const -- cgit v1.2.3 From cab9bda2496c23788c9a1575b110b2e8bdf0269c Mon Sep 17 00:00:00 2001 From: galik Date: Sat, 19 Sep 2015 07:52:30 +0100 Subject: Prevent unused parameter warnings and unknown #pragma warnings on GCC. --- include/array_view.h | 7 +++++-- include/fail_fast.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 80656a4..907bf42 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -52,10 +52,12 @@ #endif // _NOEXCEPT +#if _MSC_VER #if _MSC_VER <= 1800 #pragma warning(push) #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior #endif // _MSC_VER <= 1800 +#endif namespace Guide { @@ -547,7 +549,7 @@ namespace details // TODO : following signature is for work around VS bug template BoundsRanges (const OtherType &, bool firstLevel) {} - BoundsRanges(const SizeType * const arr) { } + BoundsRanges(const SizeType * const) { } BoundsRanges() = default; @@ -2281,9 +2283,10 @@ general_array_view_iterator operator+(typename general_array_view_ite } // namespace Guide +#if _MSC_VER #if _MSC_VER <= 1800 #pragma warning(pop) #endif // _MSC_VER <= 1800 - +#endif #pragma pop_macro("_NOEXCEPT") diff --git a/include/fail_fast.h b/include/fail_fast.h index a7385ca..382b9d6 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -39,7 +39,7 @@ inline void fail_fast_assert(bool cond, const char* const message) { if (!cond) #else inline void fail_fast_assert(bool cond) { if (!cond) std::terminate(); } -inline void fail_fast_assert(bool cond, const char* const message) { if (!cond) std::terminate(); } +inline void fail_fast_assert(bool cond, const char* const) { if (!cond) std::terminate(); } #endif // SAFER_CPP_TESTING -- cgit v1.2.3 From 05e6b6dd4ba8164a72d6968d238840340afdddb8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 20 Sep 2015 19:18:12 -0700 Subject: Added missing owner, corrected array_view::sub(). --- include/array_view.h | 10 +++++----- include/gsl.h | 3 +++ tests/CMakeLists.txt | 14 ++++++++++++++ tests/array_view_tests.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ tests/owner_tests.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 tests/owner_tests.cpp diff --git a/include/array_view.h b/include/array_view.h index 80656a4..2e061db 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1824,15 +1824,15 @@ public: template _CONSTEXPR array_view sub() const _NOEXCEPT { - static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset < bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset < this->size()) && Offset + Count <= this->size())); + static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } - _CONSTEXPR array_view sub(size_type offset, size_type count) const _NOEXCEPT + _CONSTEXPR array_view sub(size_type offset, size_type count = dynamic_range) const _NOEXCEPT { - fail_fast_assert((offset == 0 || offset < this->size()) && offset + count <= this->size()); - return { this->data() + offset, count }; + fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); + return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; } // size diff --git a/include/gsl.h b/include/gsl.h index f4c8857..cf6eca5 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -29,6 +29,9 @@ namespace Guide using std::unique_ptr; using std::shared_ptr; +template +using owner = T; + // // GSL.assert: assertions // diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dee4e28..afe24a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -142,3 +142,17 @@ add_test( NAME utils_tests COMMAND utils_tests ) + +add_executable(owner_tests + owner_tests.cpp +) +target_link_libraries(owner_tests + UnitTest++ +) +install(TARGETS owner_tests + RUNTIME DESTINATION bin +) +add_test( + NAME owner_tests + COMMAND owner_tests +) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 5c64119..ba251e8 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -1329,6 +1329,7 @@ SUITE(array_view_tests) CHECK((av.sub<2,2>().bounds() == static_bounds())); CHECK((av.sub<2,2>().length() == 2)); CHECK(av.sub(2,2).length() == 2); + CHECK(av.sub(2,3).length() == 3); } @@ -1344,15 +1345,16 @@ SUITE(array_view_tests) CHECK((av.sub<0,5>().bounds() == static_bounds())); CHECK((av.sub<0,5>().length() == 5)); CHECK(av.sub(0,5).length() == 5); + CHECK_THROW(av.sub(0,6).length(), fail_fast); + CHECK_THROW(av.sub(1,5).length(), fail_fast); } { array_view av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS CHECK((av.sub<5,0>().bounds() == static_bounds())); - CHECK((av.sub<5,0>().length() == 0)); -#endif - CHECK_THROW(av.sub(5,0).length(), fail_fast); + CHECK((av.sub<5, 0>().length() == 0)); + CHECK(av.sub(5,0).length() == 0); + CHECK_THROW(av.sub(6,0).length(), fail_fast); } { @@ -1360,8 +1362,39 @@ SUITE(array_view_tests) CHECK((av.sub<0,0>().bounds() == static_bounds())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); - } - } + CHECK_THROW((av.sub<1,0>().length()), fail_fast); + } + + { + array_view av; + CHECK(av.sub(0).length() == 0); + CHECK_THROW(av.sub(1).length(), fail_fast); + } + + { + array_view av = arr; + CHECK(av.sub(0).length() == 5); + CHECK(av.sub(1).length() == 4); + CHECK(av.sub(4).length() == 1); + CHECK(av.sub(5).length() == 0); + CHECK_THROW(av.sub(6).length(), fail_fast); + auto av2 = av.sub(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i+2); + } + + { + array_view av = arr; + CHECK(av.sub(0).length() == 5); + CHECK(av.sub(1).length() == 4); + CHECK(av.sub(4).length() == 1); + CHECK(av.sub(5).length() == 0); + CHECK_THROW(av.sub(6).length(), fail_fast); + auto av2 = av.sub(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i+2); + } + } void AssertNullEmptyProperties(array_view& av) { diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp new file mode 100644 index 0000000..430b31a --- /dev/null +++ b/tests/owner_tests.cpp @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include + +using namespace Guide; + +SUITE(owner_tests) +{ + void f(int* i) + { + *i += 1; + } + + TEST(basic_test) + { + owner p = new int(120); + CHECK(*p == 120); + f(p); + CHECK(*p == 121); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} -- cgit v1.2.3 From 65655da087f7dd32f27cf243ea3ba59609764a3a Mon Sep 17 00:00:00 2001 From: Gabriel Dos Reis Date: Mon, 21 Sep 2015 03:09:33 -0700 Subject: Tidy testing for _MSC_VER --- include/array_view.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index fb103c9..006f18c 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -52,12 +52,10 @@ #endif // _NOEXCEPT -#if _MSC_VER -#if _MSC_VER <= 1800 +#if defined(_MSC_VER) && _MSC_VER <= 1800 #pragma warning(push) #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior #endif // _MSC_VER <= 1800 -#endif namespace Guide { @@ -2283,10 +2281,8 @@ general_array_view_iterator operator+(typename general_array_view_ite } // namespace Guide -#if _MSC_VER -#if _MSC_VER <= 1800 +#if defined(_MSC_VER) && _MSC_VER <= 1800 #pragma warning(pop) #endif // _MSC_VER <= 1800 -#endif #pragma pop_macro("_NOEXCEPT") -- cgit v1.2.3 From 53b866a1097d869f00953ef5709aad3d49ca96ae Mon Sep 17 00:00:00 2001 From: Gabriel Dos Reis Date: Mon, 21 Sep 2015 08:10:05 -0700 Subject: Add additional platforms where GSL has been tested --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 763d09b..44d05e4 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,12 @@ The test suite that exercises GSL has been built and passes successfully on the * Windows using Visual Studio 2013 * Windows using Visual Studio 2015 -* Windows using Clang\LLVM 3.6 +* Windows using Clang/LLVM 3.6 * Windows using GCC 5.1 -* Linux using Clang\LLVM 3.6 -* Linux using GCC 5.1 +* GNU/Linux using Clang/LLVM 3.6 +* GNU/Linux using GCC 5.1 +* Mac OS Yosemite using XCode with AppleClang 7.0.0.7000072 +* Mac OS Yosemite using GCC-5.2.0 > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider contributing any changes that were necessary back to this project to benefit the wider community. -- cgit v1.2.3 From 7d07140cd1a93feb3ad571f09515ed8e7c2ae5fc Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Mon, 21 Sep 2015 10:30:03 -0700 Subject: Fix the broken isocpp.org link in README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 44d05e4..4678637 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # GSL: Guidelines Support Library The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the -[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](isocpp.org). +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). This repo contains Microsoft's implementation of GSL, tracking Microsoft's fork of the Guidelines. Microsoft's fork can be found here: -[C++ Core Guidelines](https://github.com/Microsoft/CppCoreGuidelines). +[C++ Core Guidelines](https://github.com/Microsoft/CppCoreGuidelines). The library includes types like `array_view<>`, `string_view<>`, `owner<>` and others. -- cgit v1.2.3 From 5d9aae7430ed15cc573b901b665dea5703a0cdeb Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 21 Sep 2015 16:04:06 -0300 Subject: Add add_gsl_test() function in tests/CMakeLists.txt With this function we can avoid duplicated code to add a new test. --- tests/CMakeLists.txt | 146 ++++++++------------------------------------------- 1 file changed, 21 insertions(+), 125 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index afe24a3..ad7a38c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,128 +31,24 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") endif() -add_executable(array_view_tests - array_view_tests.cpp -) -target_link_libraries(array_view_tests - UnitTest++ -) -install(TARGETS array_view_tests - RUNTIME DESTINATION bin -) -add_test( - NAME array_view_tests - COMMAND array_view_tests -) - -add_executable(string_view_tests - string_view_tests.cpp -) -target_link_libraries(string_view_tests - UnitTest++ -) -install(TARGETS string_view_tests - RUNTIME DESTINATION bin -) -add_test( - NAME string_view_tests - COMMAND string_view_tests -) - -add_executable(at_tests - at_tests.cpp -) -target_link_libraries(at_tests - UnitTest++ -) -install(TARGETS at_tests - RUNTIME DESTINATION bin -) -add_test( - NAME at_tests - COMMAND at_tests -) - -add_executable(bounds_tests - bounds_tests.cpp -) -target_link_libraries(bounds_tests - UnitTest++ -) -install(TARGETS bounds_tests - RUNTIME DESTINATION bin -) -add_test( - NAME bounds_tests - COMMAND bounds_tests -) - -add_executable(maybenull_tests - maybenull_tests.cpp -) -target_link_libraries(maybenull_tests - UnitTest++ -) -install(TARGETS maybenull_tests - RUNTIME DESTINATION bin -) -add_test( - NAME maybenull_tests - COMMAND maybenull_tests -) - -add_executable(notnull_tests - notnull_tests.cpp -) -target_link_libraries(notnull_tests - UnitTest++ -) -install(TARGETS notnull_tests - RUNTIME DESTINATION bin -) -add_test( - NAME notnull_tests - COMMAND notnull_tests -) - -add_executable(assertion_tests - assertion_tests.cpp -) -target_link_libraries(assertion_tests - UnitTest++ -) -install(TARGETS assertion_tests - RUNTIME DESTINATION bin -) -add_test( - NAME assertion_tests - COMMAND assertion_tests -) - -add_executable(utils_tests - utils_tests.cpp -) -target_link_libraries(utils_tests - UnitTest++ -) -install(TARGETS utils_tests - RUNTIME DESTINATION bin -) -add_test( - NAME utils_tests - COMMAND utils_tests -) - -add_executable(owner_tests - owner_tests.cpp -) -target_link_libraries(owner_tests - UnitTest++ -) -install(TARGETS owner_tests - RUNTIME DESTINATION bin -) -add_test( - NAME owner_tests - COMMAND owner_tests -) +function(add_gsl_test name) + add_executable(${name} ${name}.cpp) + target_link_libraries(${name} UnitTest++) + install(TARGETS ${name} + RUNTIME DESTINATION bin + ) + add_test( + NAME ${name} + COMMAND ${name} + ) +endfunction() + +add_gsl_test(array_view_tests) +add_gsl_test(string_view_tests) +add_gsl_test(at_tests) +add_gsl_test(bounds_tests) +add_gsl_test(maybenull_tests) +add_gsl_test(notnull_tests) +add_gsl_test(assertion_tests) +add_gsl_test(utils_tests) +add_gsl_test(owner_tests) -- cgit v1.2.3 From cb068d80cdbafa7a3b9e33bdf41a266a1c3d08bc Mon Sep 17 00:00:00 2001 From: galik Date: Mon, 21 Sep 2015 22:51:46 +0100 Subject: missing header --- include/fail_fast.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fail_fast.h b/include/fail_fast.h index 382b9d6..dfd0ede 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -17,6 +17,7 @@ #pragma once #include +#include namespace Guide { -- cgit v1.2.3 From e7c0b89ed4a730c43376f5c4af43ec5159879ac9 Mon Sep 17 00:00:00 2001 From: Andrew Pardoe Date: Mon, 21 Sep 2015 19:58:25 -0700 Subject: Removed reference to Microsoft private fork --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4678637..e34948a 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the [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, tracking Microsoft's fork of the Guidelines. Microsoft's fork can be found here: -[C++ Core Guidelines](https://github.com/Microsoft/CppCoreGuidelines). +This repo contains Microsoft's implementation of GSL. The library includes types like `array_view<>`, `string_view<>`, `owner<>` and others. -- cgit v1.2.3 From 1228e17762648fc19cb989b3c2d47f440089d101 Mon Sep 17 00:00:00 2001 From: Rico Antonio Felix Date: Tue, 22 Sep 2015 13:38:47 -0400 Subject: Corrected grammatical error... --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ce68e8..990b8e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ The Guidelines Support Library (GSL) contains functions and types that are sugge [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines. GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue -tracking the suggestion that has been approved the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted +tracking the suggestion that has been approved by the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted a PR, please post a comment in the associated issue to avoid duplication of effort. ## Legal -- cgit v1.2.3 From 9b454b7a9bc2ff2c608dcb66c4dfccdc1c40e150 Mon Sep 17 00:00:00 2001 From: Tamas Kenez Date: Wed, 23 Sep 2015 17:43:36 +0200 Subject: add travis testing (gcc5, clang36) - also relaxed CMake version to 2.8.7, the version default in travis --- .travis.yml | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 6 ++--- README.md | 18 +++++++------- tests/CMakeLists.txt | 10 ++++---- 4 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..524f1fb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# Based on https://github.com/ldionne/hana/blob/master/.travis.yml + +language: cpp +sudo: false + +matrix: + include: + - env: COMPILER=clang++-3.6 BUILD_TYPE=Debug CLANG=1 + compiler: clang + addons: &clang36 + apt: + packages: + - clang-3.6 + - cmake + sources: &sources + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + - kalakris-cmake + - env: COMPILER=clang++-3.6 BUILD_TYPE=Release CLANG=1 + compiler: clang + addons: *clang36 + - env: COMPILER=g++-5 BUILD_TYPE=Debug + compiler: gcc + addons: &gcc5 + apt: + packages: g++-5 + sources: *sources + - env: COMPILER=g++-5 BUILD_TYPE=Release + compiler: gcc + addons: *gcc5 + +install: + - which $COMPILER + - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" + - mkdir ${DEPS_DIR} && cd ${DEPS_DIR} + - | + if [[ "$CLANG" == 1 && "${TRAVIS_OS_NAME}" == "linux" && "${STDLIB}" != "libstdc++" ]]; then + if [[ "${COMPILER}" == "clang++-3.5" ]]; then LLVM_VERSION="3.5.2"; fi + if [[ "${COMPILER}" == "clang++-3.6" ]]; then LLVM_VERSION="3.6.2"; fi + if [[ "${COMPILER}" == "clang++-3.7" ]]; then LLVM_VERSION="3.7.0"; fi + LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz" + LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz" + LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz" + mkdir -p llvm llvm/build llvm/projects/libcxx llvm/projects/libcxxabi + travis_retry wget --quiet -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C llvm + travis_retry wget --quiet -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxx + travis_retry wget --quiet -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxxabi + (cd llvm/build && cmake .. -DCMAKE_INSTALL_PREFIX=${DEPS_DIR}/llvm/install -DCMAKE_CXX_COMPILER=clang++) + (cd llvm/build/projects/libcxx && make install -j2) + (cd llvm/build/projects/libcxxabi && make install -j2) + export CXXFLAGS="-I ${DEPS_DIR}/llvm/install/include/c++/v1" + export LDFLAGS="-L ${DEPS_DIR}/llvm/install/lib -l c++ -l c++abi" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${DEPS_DIR}/llvm/install/lib" + fi + +before_script: + - cd ${TRAVIS_BUILD_DIR} + - git clone --depth 1 https://github.com/Microsoft/unittest-cpp tests/unittest-cpp + - cmake -H. -Bb -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_INSTALL_PREFIX=$PWD/o -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - cmake --build b + +script: + - cd b + - ctest + +notifications: + email: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 2125f7b..f8145d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 3.2.2) +cmake_minimum_required(VERSION 2.8.7) -project(GSL) +project(GSL CXX) include_directories( ${CMAKE_CURRENT_BINARY_DIR} @@ -8,4 +8,4 @@ include_directories( enable_testing() -add_subdirectory(tests) \ No newline at end of file +add_subdirectory(tests) diff --git a/README.md b/README.md index e34948a..300b103 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# GSL: Guidelines Support Library +# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the -[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). This repo contains Microsoft's implementation of GSL. The library includes types like `array_view<>`, `string_view<>`, `owner<>` and others. @@ -12,7 +12,7 @@ While some types have been broken out into their own headers (e.g. [include/arra 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 -other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. +other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. # Quick Start ## Supported Platforms @@ -27,13 +27,13 @@ The test suite that exercises GSL has been built and passes successfully on the * Mac OS Yosemite using XCode with AppleClang 7.0.0.7000072 * Mac OS Yosemite using GCC-5.2.0 -> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider -contributing any changes that were necessary back to this project to benefit the wider community. +> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider +contributing any changes that were necessary back to this project to benefit the wider community. ## Building the tests To build the tests, you will require the following: -* [CMake](http://cmake.org), version 3.3 or later to be installed and in your PATH. +* [CMake](http://cmake.org), version 2.8.7 or later to be installed and in your PATH. * [UnitTest-cpp](https://github.com/Microsoft/unittest-cpp), to be cloned under the [tests/unittest-cpp](./tests/unittest-cpp) directory of your GSL source. @@ -46,13 +46,13 @@ These steps assume the source code of this repository has been cloned into a dir cd build-x86 2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`). - + cmake -G "Visual Studio 14 2015" c:\GSL - + 3. Build the test suite (in this case, in the Debug configuration, Release is another good choice). cmake --build . --config Debug - + 4. Run the test suite. ctest -C Debug diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ad7a38c..0415db3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,6 @@ -cmake_minimum_required(VERSION 3.2.2) +cmake_minimum_required(VERSION 2.8.7) -project(GSLTests) +project(GSLTests CXX) add_subdirectory(unittest-cpp) @@ -23,7 +23,7 @@ else() elseif(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else() - message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() endif() @@ -38,8 +38,8 @@ function(add_gsl_test name) RUNTIME DESTINATION bin ) add_test( - NAME ${name} - COMMAND ${name} + ${name} + ${name} ) endfunction() -- cgit v1.2.3 From c3b85f9571d43b8330dba8d08cc8a8e85af67059 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 23 Sep 2015 22:12:49 +0200 Subject: Fixes spelling of Xcode --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e34948a..c169d19 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The test suite that exercises GSL has been built and passes successfully on the * Windows using GCC 5.1 * GNU/Linux using Clang/LLVM 3.6 * GNU/Linux using GCC 5.1 -* Mac OS Yosemite using XCode with AppleClang 7.0.0.7000072 +* Mac OS Yosemite using Xcode with AppleClang 7.0.0.7000072 * Mac OS Yosemite using GCC-5.2.0 > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider -- cgit v1.2.3 From 2c8886bdfb532702484ee0c8d28c5b483c34fb6f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 23 Sep 2015 23:18:48 +0200 Subject: Also renames MAC OS to OS X --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c169d19..506110f 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ The test suite that exercises GSL has been built and passes successfully on the * Windows using GCC 5.1 * GNU/Linux using Clang/LLVM 3.6 * GNU/Linux using GCC 5.1 -* Mac OS Yosemite using Xcode with AppleClang 7.0.0.7000072 -* Mac OS Yosemite using GCC-5.2.0 +* OS X Yosemite using Xcode with AppleClang 7.0.0.7000072 +* OS X Yosemite using GCC-5.2.0 > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider contributing any changes that were necessary back to this project to benefit the wider community. -- cgit v1.2.3 From 996aa06e08163aa8a53105921702c09d55d20dec Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Thu, 24 Sep 2015 14:09:40 -0700 Subject: Fix issue #45: comparing two maybe_null_dbg's can cause fail_fast --- include/gsl.h | 2 ++ tests/maybenull_tests.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/include/gsl.h b/include/gsl.h index cf6eca5..1357d76 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -202,6 +202,8 @@ public: bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } bool operator!=(const T& rhs) const { return !(*this == rhs); } + bool operator==(const maybe_null_dbg& rhs) const { tested_ = true; rhs.tested_ = true; return ptr_ == rhs.ptr_; } + bool operator!=(const maybe_null_dbg& rhs) const { return !(*this == rhs); } T get() const { fail_fast_assert(tested_); diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 1fdfb78..0a9d891 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -189,6 +189,58 @@ SUITE(MaybeNullTests) CHECK(q.present()); CHECK(q->foo()); } + + TEST(TestMaybeNullCompare) + { + int i1 = 1; + int i2 = 2; + + maybe_null_dbg p1 = &i1; + maybe_null_dbg p1_2 = &i1; + maybe_null_dbg p2 = &i2; + + CHECK_THROW(p1.get(), fail_fast); + CHECK_THROW(p1_2.get(), fail_fast); + CHECK_THROW(p2.get(), fail_fast); + + CHECK(p1 != p2); + CHECK(!(p1 == p2)); + CHECK(p1 == p1); + CHECK(p1 == p1_2); + + // Make sure we no longer throw here + CHECK(p1.get() != nullptr); + CHECK(p1_2.get() != nullptr); + CHECK(p2.get() != nullptr); + } + + TEST(TestMaybeNullCopy) + { + int i1 = 1; + int i2 = 2; + + maybe_null_dbg p1 = &i1; + maybe_null_dbg p1_2 = &i1; + maybe_null_dbg p2 = &i2; + + CHECK(p1 != p2); + CHECK(p1 == p1_2); + + // Make sure we no longer throw here + CHECK(p1.get() != nullptr); + CHECK(p2.get() != nullptr); + + p1 = p2; + + // Make sure we now throw + CHECK_THROW(p1.get(), fail_fast); + + CHECK(p1 == p2); + CHECK(p1 != p1_2); + + // Make sure we no longer throw here + CHECK(p1.get() != nullptr); + } } int main(int, const char *[]) -- cgit v1.2.3 From 35fb11853ff06d07805b121d928a1ba270532d1a Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Thu, 24 Sep 2015 18:02:37 -0700 Subject: Fix issues #48 #49 #50 --- include/gsl.h | 15 ++++++++++----- tests/maybenull_tests.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 1357d76..b78c6e2 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -162,7 +162,11 @@ private: template class maybe_null_dbg { + template + friend class maybe_null_dbg; public: + static_assert(std::is_constructible::value, "maybe_null's template parameter must be constructible from nullptr"); + maybe_null_dbg() : ptr_(nullptr), tested_(false) {} maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} @@ -202,8 +206,10 @@ public: bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } bool operator!=(const T& rhs) const { return !(*this == rhs); } - bool operator==(const maybe_null_dbg& rhs) const { tested_ = true; rhs.tested_ = true; return ptr_ == rhs.ptr_; } - bool operator!=(const maybe_null_dbg& rhs) const { return !(*this == rhs); } + template ::value>> + bool operator==(const maybe_null_dbg& rhs) const { tested_ = true; rhs.tested_ = true; return ptr_ == rhs.ptr_; } + template ::value>> + bool operator!=(const maybe_null_dbg& rhs) const { return !(*this == rhs); } T get() const { fail_fast_assert(tested_); @@ -217,8 +223,6 @@ public: T operator->() const { return get(); } private: - const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type - // unwanted operators...pointers only point to single objects! // TODO ensure all arithmetic ops on this type are unavailable maybe_null_dbg& operator++() = delete; @@ -238,6 +242,8 @@ template class maybe_null_ret { public: + static_assert(std::is_constructible::value, "maybe_null's template parameter must be constructible from nullptr"); + maybe_null_ret() : ptr_(nullptr) {} maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} maybe_null_ret(const T& p) : ptr_(p) {} @@ -280,7 +286,6 @@ private: maybe_null_ret& operator-(size_t) = delete; maybe_null_ret& operator-=(size_t) = delete; - const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type T ptr_; }; diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 0a9d891..e1244fd 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -241,6 +241,20 @@ SUITE(MaybeNullTests) // Make sure we no longer throw here CHECK(p1.get() != nullptr); } + + TEST(TestMaybeNullPtrT) + { + maybe_null p1; + maybe_null p2; + + CHECK_THROW(p1.get(), fail_fast); + + CHECK(p1 == p2); + + // Make sure we no longer throw here + CHECK(p1.get() == nullptr); + CHECK(p2.get() == nullptr); + } } int main(int, const char *[]) -- cgit v1.2.3 From 51da13607c655e8b57e82d1243b9283421182c27 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Thu, 24 Sep 2015 18:08:34 -0700 Subject: Fix issue #39: Add header guards --- include/array_view.h | 5 +++++ include/fail_fast.h | 5 +++++ include/gsl.h | 5 +++++ include/string_view.h | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/include/array_view.h b/include/array_view.h index 006f18c..26a1641 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -16,6 +16,9 @@ #pragma once +#ifndef GSL_ARRAY_VIEW_H +#define GSL_ARRAY_VIEW_H + #include #include #include @@ -2286,3 +2289,5 @@ general_array_view_iterator operator+(typename general_array_view_ite #endif // _MSC_VER <= 1800 #pragma pop_macro("_NOEXCEPT") + +#endif // GSL_ARRAY_VIEW_H diff --git a/include/fail_fast.h b/include/fail_fast.h index dfd0ede..78a5102 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -16,6 +16,9 @@ #pragma once +#ifndef GSL_FAIL_FAST_H +#define GSL_FAIL_FAST_H + #include #include @@ -45,3 +48,5 @@ inline void fail_fast_assert(bool cond, const char* const) { if (!cond) std::ter #endif // SAFER_CPP_TESTING } + +#endif // GSL_FAIL_FAST_H diff --git a/include/gsl.h b/include/gsl.h index 1357d76..09a46c7 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -16,6 +16,9 @@ #pragma once +#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 @@ -287,3 +290,5 @@ private: template using maybe_null = maybe_null_ret; } // namespace Guide + +#endif // GSL_GSL_H diff --git a/include/string_view.h b/include/string_view.h index 7becc8e..e683c7a 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -16,6 +16,9 @@ #pragma once +#ifndef GSL_STRING_VIEW_H +#define GSL_STRING_VIEW_H + #include "array_view.h" #include @@ -176,3 +179,5 @@ using zstring_builder = basic_zstring_builder; template using wzstring_builder = basic_zstring_builder; } + +#endif // GSL_STRING_VIEW_H -- cgit v1.2.3 From e1570268077b2b063f7397a804fe17690b2ddc9e Mon Sep 17 00:00:00 2001 From: Kern Handa Date: Fri, 25 Sep 2015 00:42:38 -0700 Subject: Rank and dimensions should be size_t. --- include/array_view.h | 132 +++++++++++++++++++++++++-------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 006f18c..f9eadc4 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -71,20 +71,20 @@ namespace details }; - template + template class coordinate_facade { static_assert(std::is_integral::value && sizeof(ValueType) <= sizeof(size_t), "ValueType must be unsigned integral type!"); static_assert(Rank > 0, "Rank must be greater than 0!"); - template + template friend class coordinate_facade; public: using reference = ValueType&; using const_reference = const ValueType&; using value_type = ValueType; - static const unsigned int rank = Rank; + static const size_t rank = Rank; _CONSTEXPR coordinate_facade() _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); @@ -92,7 +92,7 @@ namespace details _CONSTEXPR coordinate_facade(const value_type(&values)[rank]) _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) elems[i] = values[i]; } _CONSTEXPR coordinate_facade(value_type e0) _NOEXCEPT @@ -106,7 +106,7 @@ namespace details { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { elems[i] = begin(il)[i]; } @@ -117,7 +117,7 @@ namespace details template _CONSTEXPR coordinate_facade(const coordinate_facade & other) { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); elems[i] = static_cast(other.elems[i]); @@ -126,20 +126,20 @@ namespace details protected: coordinate_facade& operator=(const coordinate_facade& rhs) = default; // Preconditions: component_idx < rank - _CONSTEXPR reference operator[](unsigned int component_idx) + _CONSTEXPR reference operator[](size_t component_idx) { fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } // Preconditions: component_idx < rank - _CONSTEXPR const_reference operator[](unsigned int component_idx) const + _CONSTEXPR const_reference operator[](size_t component_idx) const { fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { if (elems[i] != rhs.elems[i]) return false; @@ -157,7 +157,7 @@ namespace details _CONSTEXPR ConcreteType operator-() const { ConcreteType ret = to_concrete(); - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) ret.elems[i] = -ret.elems[i]; return ret; } @@ -175,13 +175,13 @@ namespace details } _CONSTEXPR ConcreteType& operator+=(const ConcreteType& rhs) { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) elems[i] += rhs.elems[i]; return to_concrete(); } _CONSTEXPR ConcreteType& operator-=(const ConcreteType& rhs) { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) elems[i] -= rhs.elems[i]; return to_concrete(); } @@ -229,13 +229,13 @@ namespace details } _CONSTEXPR ConcreteType& operator*=(value_type v) { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) elems[i] *= v; return to_concrete(); } _CONSTEXPR ConcreteType& operator/=(value_type v) { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) elems[i] /= v; return to_concrete(); } @@ -270,12 +270,12 @@ namespace details }; } -template +template class index : private details::coordinate_facade, ValueType, Rank> { using Base = details::coordinate_facade, ValueType, Rank>; friend Base; - template + template friend class index; public: using Base::rank; @@ -317,10 +317,10 @@ public: template class index<1, ValueType> { - template + template friend class index; public: - static const unsigned int rank = 1; + static const size_t rank = 1; using reference = ValueType&; using const_reference = const ValueType&; using size_type = ValueType; @@ -537,8 +537,8 @@ namespace details template struct BoundsRanges { - static const unsigned int Depth = 0; - static const unsigned int DynamicNum = 0; + static const size_t Depth = 0; + static const size_t DynamicNum = 0; static const SizeType CurrentRange = 1; static const SizeType TotalSize = 1; @@ -551,14 +551,14 @@ namespace details BoundsRanges() = default; - template + template void serialize(T &) const { } - template + template SizeType linearize(const T &) const { return 0; } - template + template ptrdiff_t contains(const T &) const { return 0; } @@ -576,8 +576,8 @@ namespace details template struct BoundsRanges : BoundsRanges{ using Base = BoundsRanges ; - static const unsigned int Depth = Base::Depth + 1; - static const unsigned int DynamicNum = Base::DynamicNum + 1; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; static const SizeType CurrentRange = dynamic_range; static const SizeType TotalSize = dynamic_range; const SizeType m_bound; @@ -596,19 +596,19 @@ namespace details { } - template + template void serialize(T & arr) const { arr[Dim] = elementNum(); this->Base::template serialize(arr); } - template + template SizeType linearize(const T & arr) const { const size_t index = this->Base::totalSize() * arr[Dim]; fail_fast_assert(index < static_cast(m_bound)); return static_cast(index) + this->Base::template linearize(arr); } - template + template ptrdiff_t contains(const T & arr) const { const ptrdiff_t last = this->Base::template contains(arr); if (last == -1) @@ -625,7 +625,7 @@ namespace details return static_cast(totalSize() / this->Base::totalSize()); } - SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + SizeType elementNum(size_t dim) const _NOEXCEPT{ if (dim > 0) return this->Base::elementNum(dim - 1); else @@ -641,8 +641,8 @@ namespace details template struct BoundsRanges : BoundsRanges{ using Base = BoundsRanges ; - static const unsigned int Depth = Base::Depth + 1; - static const unsigned int DynamicNum = Base::DynamicNum; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; static const SizeType CurrentRange = static_cast(CurRange); static const SizeType TotalSize = StaticSizeHelper::value; static_assert (CurRange <= SizeTypeTraits::max_value, "CurRange must be smaller than SizeType limits"); @@ -657,19 +657,19 @@ namespace details fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); } - template + template void serialize(T & arr) const { arr[Dim] = elementNum(); this->Base::template serialize(arr); } - template + template SizeType linearize(const T & arr) const { fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); } - template + template ptrdiff_t contains(const T & arr) const { if (static_cast(arr[Dim]) >= CurrentRange) return -1; @@ -687,7 +687,7 @@ namespace details return CurrentRange; } - SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + SizeType elementNum(size_t dim) const _NOEXCEPT{ if (dim > 0) return this->Base::elementNum(dim - 1); else @@ -732,17 +732,17 @@ namespace details { const TypeChain & obj; TypeListIndexer(const TypeChain & obj) :obj(obj){} - template + template const TypeChain & getObj(std::true_type) { return obj; } - template + template auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) { return TypeListIndexer(static_cast(obj)).template get(); } - template + template auto get() -> decltype(getObj(std::integral_constant())) { return getObj(std::integral_constant()); @@ -779,8 +779,8 @@ class static_bounds template friend class static_bounds; public: - static const unsigned int rank = MyRanges::Depth; - static const unsigned int dynamic_rank = MyRanges::DynamicNum; + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; static const SizeType static_size = static_cast(MyRanges::TotalSize); using size_type = SizeType; @@ -844,12 +844,12 @@ public: return m_ranges.contains(idx) != -1; } - _CONSTEXPR size_type operator[](unsigned int index) const _NOEXCEPT + _CONSTEXPR size_type operator[](size_t index) const _NOEXCEPT { return m_ranges.elementNum(index); } - template + template _CONSTEXPR size_type extent() const _NOEXCEPT { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); @@ -888,12 +888,12 @@ public: } }; -template +template class strided_bounds : private details::coordinate_facade, SizeType, Rank> { using Base = details::coordinate_facade, SizeType, Rank>; friend Base; - template + template friend class strided_bounds; public: @@ -921,7 +921,7 @@ public: _CONSTEXPR strided_bounds(const index_type &extents, const index_type &strides) : m_strides(strides) { - for (unsigned int i = 0; i < rank; i++) + for (size_t i = 0; i < rank; i++) Base::elems[i] = extents[i]; } _CONSTEXPR strided_bounds(const value_type(&values)[rank], index_type strides) @@ -935,20 +935,20 @@ public: _CONSTEXPR size_type total_size() const _NOEXCEPT { size_type ret = 0; - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) ret += (Base::elems[i] - 1) * m_strides[i]; return ret + 1; } _CONSTEXPR size_type size() const _NOEXCEPT { size_type ret = 1; - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) ret *= Base::elems[i]; return ret; } _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { if (idx[i] < 0 || idx[i] >= Base::elems[i]) return false; @@ -958,7 +958,7 @@ public: _CONSTEXPR size_type linearize(const index_type & idx) const { size_type ret = 0; - for (unsigned int i = 0; i < rank; i++) + for (size_t i = 0; i < rank; i++) { fail_fast_assert(idx[i] < Base::elems[i], "index is out of bounds of the array"); ret += idx[i] * m_strides[i]; @@ -974,7 +974,7 @@ public: { return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; } - template + template _CONSTEXPR size_type extent() const _NOEXCEPT { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); @@ -1000,7 +1000,7 @@ template struct is_bounds : std::integral_constant {}; template struct is_bounds> : std::integral_constant {}; -template +template struct is_bounds> : std::integral_constant {}; template @@ -1014,7 +1014,7 @@ class bounds_iterator private: using Base = std::iterator , const IndexType>; public: - static const unsigned int rank = IndexType::rank; + static const size_t rank = IndexType::rank; using typename Base::reference; using typename Base::pointer; using typename Base::difference_type; @@ -1038,7 +1038,7 @@ public: } bounds_iterator& operator++() _NOEXCEPT { - for (unsigned int i = rank; i-- > 0;) + for (size_t i = rank; i-- > 0;) { if (++curr[i] < boundary[i]) { @@ -1050,7 +1050,7 @@ public: } } // If we're here we've wrapped over - set to past-the-end. - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { curr[i] = boundary[i]; } @@ -1096,11 +1096,11 @@ public: auto linear_idx = linearize(curr) + n; value_type stride; stride[rank - 1] = 1; - for (unsigned int i = rank - 1; i-- > 0;) + for (size_t i = rank - 1; i-- > 0;) { stride[i] = stride[i + 1] * boundary[i + 1]; } - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; @@ -1134,7 +1134,7 @@ public: } bool operator<(const bounds_iterator& rhs) const _NOEXCEPT { - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { if (curr[i] < rhs.curr[i]) return true; @@ -1164,7 +1164,7 @@ private: // TODO: Smarter impl. // Check if past-the-end bool pte = true; - for (unsigned int i = 0; i < rank; ++i) + for (size_t i = 0; i < rank; ++i) { if (idx[i] != boundary[i]) { @@ -1177,7 +1177,7 @@ private: if (pte) { res = 1; - for (unsigned int i = rank; i-- > 0;) + for (size_t i = rank; i-- > 0;) { res += (idx[i] - 1) * multiplier; multiplier *= boundary[i]; @@ -1185,7 +1185,7 @@ private: } else { - for (unsigned int i = rank; i-- > 0;) + for (size_t i = rank; i-- > 0;) { res += idx[i] * multiplier; multiplier *= boundary[i]; @@ -1359,7 +1359,7 @@ template class basic_array_view { public: - static const unsigned int rank = BoundsType::rank; + static const size_t rank = BoundsType::rank; using bounds_type = BoundsType; using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; @@ -1381,7 +1381,7 @@ public: { return m_bounds; } - template + template _CONSTEXPR size_type extent() const _NOEXCEPT { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); @@ -1537,7 +1537,7 @@ struct dim template class array_view; -template +template class strided_array_view; namespace details @@ -1616,7 +1616,7 @@ namespace details template struct is_array_view_oracle> : std::true_type {}; - template + template struct is_array_view_oracle> : std::true_type {}; template @@ -1930,12 +1930,12 @@ template _CONSTEXPR auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, array_view, dynamic_range>> = delete; -template +template class strided_array_view : public basic_array_view::value_type, strided_bounds::size_type>> { using Base = basic_array_view::value_type, strided_bounds::size_type>>; - template + template friend class strided_array_view; public: using Base::rank; -- cgit v1.2.3 From 1a791992a0bc4b86bedd55768da9585ad9c08e70 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Fri, 25 Sep 2015 12:16:39 -0700 Subject: Add equality operators to maybe_nul_ret --- include/gsl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/gsl.h b/include/gsl.h index b78c6e2..009c777 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -267,6 +267,9 @@ public: maybe_null_ret& operator=(const T& p) { if (ptr_ != p) { ptr_ = p; } return *this; } maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return ptr_ != rhs; } + bool present() const { return ptr_ != nullptr; } T get() const { return ptr_; } -- cgit v1.2.3 From c4f9b87d96176c32ae609c293816dbbe732f2899 Mon Sep 17 00:00:00 2001 From: Kern Handa Date: Fri, 25 Sep 2015 17:01:29 -0700 Subject: We should be using standard algorithms where possible. Use of algorithms in the STL should be promoted where possible. Also fixed up some whitespace issues. --- include/array_view.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 26a1641..d8b5cab 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "fail_fast.h" #ifndef _MSC_VER @@ -142,12 +143,7 @@ namespace details } _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT { - for (unsigned int i = 0; i < rank; ++i) - { - if (elems[i] != rhs.elems[i]) - return false; - } - return true; + return std::equal(elems, elems + rank, rhs.elems); } _CONSTEXPR bool operator!=(const ConcreteType& rhs) const _NOEXCEPT { @@ -160,8 +156,7 @@ namespace details _CONSTEXPR ConcreteType operator-() const { ConcreteType ret = to_concrete(); - for (unsigned int i = 0; i < rank; ++i) - ret.elems[i] = -ret.elems[i]; + std::transform(ret, ret + rank, ret, std::negate{}); return ret; } _CONSTEXPR ConcreteType operator+(const ConcreteType& rhs) const -- cgit v1.2.3 From 96e57571813988af7ec1706d91a56e01d577782f Mon Sep 17 00:00:00 2001 From: Seth Cantrell Date: Sat, 26 Sep 2015 18:00:54 -0400 Subject: Add .clang-format file To have any hope of achieving consistent formatting there needs to be a standard and a mechanism for enforcing it. This is a first step in that direction. This format specification is intended to mimic the existing style insofar as the current source has any consistent style. For example the determination as to whether ref qualifiers should bind to the type or variable name was made by trying both and seeing which produced fewer changes. One exception is the use of tabs vs. spaces, which was decided based on a comment by neilmacintosh [here][1]. To use clang-format with Visual Studio [download][2] and install the clang-format plugin. In VS ctrl-r, ctrl-f formats the current line or text selection. [1]: https://github.com/Microsoft/GSL/issues/55 [2]: http:llvm.org/builds/ --- .clang-format | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..bef7ebc --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +ColumnLimit: 0 + +UseTab: Never +IndentWidth: 4 +AccessModifierOffset: -4 +NamespaceIndentation: Inner + +BreakBeforeBraces: Allman +AlwaysBreakTemplateDeclarations: false +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true + +PointerAlignment: Left +AlignConsecutiveAssignments: false +AlignTrailingComments: false + +SpaceAfterCStyleCast: true -- cgit v1.2.3 From 437791e504462b2585808a717b61e084692638be Mon Sep 17 00:00:00 2001 From: saurabh singh Date: Sun, 27 Sep 2015 16:11:12 +0530 Subject: GSL::finally can make use of move semantics for eg consider this case [code] string value = "someVeryLongErrorMessageIAm"; finally([value] { PrintErrorMessage(value); } [/code] With the current changes before the call to PrintErrorMessage there will be 3 calls to copy constructor for string(1 when it's captured in closure, 2nd when finally is called and 3rd when it's passed to Final_act . With my patch there will be 1 call to the copy constructor and 2 to the move constructor for the scenario in example, so 2 potential deep copies will be saved for some objects. Validated that code builds from root, and all tests pass after my change. Also validated that indeed copy constructor calls are saved for objects that support move semantics. --- include/gsl.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 09a46c7..23b62a9 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -50,7 +50,7 @@ template class Final_act { public: - explicit Final_act(F f) : f_(f) {} + explicit Final_act(F f) : f_(std::move(f)) {} Final_act(const Final_act&& other) : f_(other.f_) {} Final_act(const Final_act&) = delete; @@ -64,7 +64,10 @@ private: // finally() - convenience function to generate a Final_act template -Final_act finally(F f) { return Final_act(f); } +Final_act finally(const F &f) { return Final_act(f); } + +template +Final_act finally(F &&f) { return Final_act(std::forward(f)); } // narrow_cast(): a searchable way to do narrowing casts of values template -- cgit v1.2.3 From 2b6d90436f3f0b0444efc918203663367e823b65 Mon Sep 17 00:00:00 2001 From: Kern Handa Date: Fri, 25 Sep 2015 09:41:40 -0700 Subject: not_null and maybe_null variants should only work on nullptr-assignable types. This is in accordance with the GSL.View guidance on not_null and maybe_null types in the CppCoreGuidelines document. --- include/gsl.h | 3 +++ tests/maybenull_tests.cpp | 13 +++++++++++++ tests/notnull_tests.cpp | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/include/gsl.h b/include/gsl.h index 23b62a9..fed3b6c 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -108,6 +108,7 @@ typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index template class not_null { + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); public: not_null(T t) : ptr_(t) { ensure_invariant(); } @@ -168,6 +169,7 @@ private: template class maybe_null_dbg { + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); public: maybe_null_dbg() : ptr_(nullptr), tested_(false) {} @@ -243,6 +245,7 @@ private: template class maybe_null_ret { + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); public: maybe_null_ret() : ptr_(nullptr) {} maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 0a9d891..4b74114 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -16,6 +16,7 @@ #include #include +#include using namespace Guide; @@ -27,12 +28,24 @@ SUITE(MaybeNullTests) { TEST(TestMaybeNull1) { +#ifdef CONFIRM_COMPILATION_ERRORS + // Forbid non-nullptr assignable types + maybe_null_ret> f_ret(std::vector{1}); + maybe_null_ret> f_ret(std::vector{1}); + maybe_null_ret z_ret(10); + maybe_null_dbg> y_dbg({1,2}); + maybe_null_dbg z_dbg(10); + maybe_null_dbg> y_dbg({1,2}); +#endif int n = 5; maybe_null_dbg opt_n(&n); int result = 0; bool threw = false; CHECK_THROW(result = *opt_n, fail_fast); + + maybe_null_ret> x_ret(std::make_shared(10)); // shared_ptr is nullptr assignable + maybe_null_dbg> x_dbg(std::make_shared(10)); // shared_ptr is nullptr assignable } TEST(TestMaybeNull2) diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index 008cbb3..7232840 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -16,6 +16,7 @@ #include #include +#include using namespace Guide; @@ -48,11 +49,18 @@ SUITE(NotNullTests) not_null p; // yay...does not compile! std::unique_ptr up = std::make_unique(120); not_null p = up; + + // Forbid non-nullptr assignable types + not_null> f(std::vector{1}); + not_null z(10); + not_null> y({1,2}); #endif int i = 12; auto rp = RefCounted(&i); not_null p(rp); CHECK(p.get() == &i); + + not_null> x(std::make_shared(10)); // shared_ptr is nullptr assignable } TEST(TestNotNullCasting) -- cgit v1.2.3 From fb91393bb205ea6a19cda4c92db5899519be9bdc Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 27 Sep 2015 16:25:43 -0700 Subject: Fixing size_t/int mismatch in loops. --- include/array_view.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index e40918d..68a3cf0 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1067,7 +1067,7 @@ public: } bounds_iterator& operator--() _NOEXCEPT { - for (int i = rank; i-- > 0;) + for (size_t i = rank; i-- > 0;) { if (curr[i]-- > 0) { @@ -1335,7 +1335,7 @@ namespace details auto extents = bnd.index_bounds(); typename Bounds::index_type stride; stride[Bounds::rank - 1] = 1; - for (int i = Bounds::rank - 2; i >= 0; --i) + for (size_t i = Bounds::rank - 2; i >= 0; --i) stride[i] = stride[i + 1] * extents[i + 1]; return stride; } @@ -2035,7 +2035,7 @@ private: fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); - for (int i = rank - 2; i >= 0; --i) + for (size_t i = rank - 2; i >= 0; --i) { fail_fast_assert((strides[i] >= strides[i + 1]) && (strides[i] % strides[i + 1] == 0), "Only strided arrays with regular strides can be resized"); } -- cgit v1.2.3 From 99746e2d57a3068d1130dce4bf5b1e46a86cf9d2 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 27 Sep 2015 16:53:58 -0700 Subject: Correct fix for int/size_t mismatch. --- include/array_view.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 68a3cf0..038b6e4 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1328,15 +1328,15 @@ namespace details return bnd.strides(); } - // Make a stride vector from bounds, assuming continugous memory. + // Make a stride vector from bounds, assuming contiguous memory. template _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT { auto extents = bnd.index_bounds(); typename Bounds::index_type stride; stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 2; i >= 0; --i) - stride[i] = stride[i + 1] * extents[i + 1]; + for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) + stride[i-1] = stride[i] * extents[i]; return stride; } @@ -2035,10 +2035,8 @@ private: fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); - for (size_t i = rank - 2; i >= 0; --i) - { - fail_fast_assert((strides[i] >= strides[i + 1]) && (strides[i] % strides[i + 1] == 0), "Only strided arrays with regular strides can be resized"); - } + for (size_t i = rank - 1; i > 0; --i) + fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); index_type ret = strides / d; ret[rank - 1] = 1; -- cgit v1.2.3 From bb169976da75c4c8a30b403ffc8ff887d72a75bf Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 27 Sep 2015 18:06:51 -0700 Subject: Fixed leak in owner<> test. Ha ha ha! --- tests/owner_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index 430b31a..d985533 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -33,6 +33,7 @@ SUITE(owner_tests) CHECK(*p == 120); f(p); CHECK(*p == 121); + delete p; } } -- cgit v1.2.3 From 783eaabf9d7b40403540f2164fb096c3150d4c94 Mon Sep 17 00:00:00 2001 From: Kern Handa Date: Mon, 28 Sep 2015 07:35:18 +0000 Subject: Add various copy assignment operators to not_null and maybe_null_*. Also removed unused constant member variable that seemed to be there to prevent maybe_null_* being used with anything other than a pointer, which is being taken care of with a static_assert now. --- include/gsl.h | 105 ++++++++++++++++++++++++++++++++++------------ tests/maybenull_tests.cpp | 38 +++++++++++++++++ tests/notnull_tests.cpp | 11 ++++- 3 files changed, 126 insertions(+), 28 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index fed3b6c..bf3573e 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -111,21 +111,27 @@ class not_null static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); public: not_null(T t) : ptr_(t) { ensure_invariant(); } - - // deleting these two prevents compilation when initialized with a nullptr or literal 0 - not_null(std::nullptr_t) = delete; - not_null(int) = delete; + not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } not_null(const not_null &other) = default; + not_null& operator=(const not_null &other) = default; template ::value>> - not_null(const not_null &other) : ptr_(other.get()) + not_null(const not_null &other) { + *this = other; } - not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } + template ::value>> + not_null& operator=(const not_null &other) + { + ptr_ = other.get(); + return *this; + } // prevents compilation when someone attempts to assign a nullptr + not_null(std::nullptr_t) = delete; + not_null(int) = delete; not_null& operator=(std::nullptr_t) = delete; not_null& operator=(int) = delete; @@ -166,26 +172,18 @@ private: // // Describes an optional pointer - provides symmetry with not_null // +template +class maybe_null_ret; + template class maybe_null_dbg { static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); public: maybe_null_dbg() : ptr_(nullptr), tested_(false) {} + maybe_null_dbg(std::nullptr_t) : ptr_(nullptr), tested_(false) {} maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} - maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} - - template ::value>> - maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) - { - } - - template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) - { - } - maybe_null_dbg& operator=(const T& p) { if (ptr_ != p) @@ -196,6 +194,8 @@ public: return *this; } + + maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} maybe_null_dbg& operator=(const maybe_null_dbg& rhs) { if (this != &rhs) @@ -206,6 +206,43 @@ public: return *this; } + + template ::value>> + maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) {} + + template ::value>> + maybe_null_dbg& operator=(const not_null &other) + { + ptr_ = other.get(); + tested_ = false; + return *this; + } + + + template ::value>> + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) {} + + template ::value>> + maybe_null_dbg& operator=(const maybe_null_dbg &other) + { + ptr_ = other.get(); + tested_ = false; + return *this; + } + + + template ::value>> + maybe_null_dbg(const maybe_null_ret &other) : ptr_(other.get()), tested_(false) {} + + template ::value>> + maybe_null_dbg& operator=(const maybe_null_ret &other) + { + ptr_ = other.get(); + tested_ = false; + return *this; + } + + bool present() const { tested_ = true; return ptr_ != nullptr; } bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } @@ -225,8 +262,6 @@ public: T operator->() const { return get(); } private: - const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type - // unwanted operators...pointers only point to single objects! // TODO ensure all arithmetic ops on this type are unavailable maybe_null_dbg& operator++() = delete; @@ -249,26 +284,45 @@ class maybe_null_ret public: maybe_null_ret() : ptr_(nullptr) {} maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} + maybe_null_ret(const T& p) : ptr_(p) {} + maybe_null_ret& operator=(const T& p) { ptr_ = p; return *this; } + maybe_null_ret(const maybe_null_ret& rhs) = default; + maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; template ::value>> - maybe_null_ret(const not_null &other) : ptr_(other.get()) + maybe_null_ret(const not_null &other) : ptr_(other.get()) {} + + template ::value>> + maybe_null_ret& operator=(const not_null &other) { + ptr_ = other.get(); + return *this; } + template ::value>> - maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) + maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) {} + + template ::value>> + maybe_null_ret& operator=(const maybe_null_ret &other) { + ptr_ = other.get(); + return *this; } + template ::value>> - maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) + maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) {} + + template ::value>> + maybe_null_ret& operator=(const maybe_null_dbg &other) { + ptr_ = other.get(); + return *this; } - maybe_null_ret& operator=(const T& p) { if (ptr_ != p) { ptr_ = p; } return *this; } - maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; bool present() const { return ptr_ != nullptr; } @@ -289,7 +343,6 @@ private: maybe_null_ret& operator-(size_t) = delete; maybe_null_ret& operator-=(size_t) = delete; - const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type T ptr_; }; diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 4b74114..d6b53f0 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace Guide; @@ -254,6 +255,43 @@ SUITE(MaybeNullTests) // Make sure we no longer throw here CHECK(p1.get() != nullptr); } + + TEST(TestMaybeNullAssignmentOps) + { + MyBase base; + MyDerived derived; + Unrelated unrelated; + + not_null nnBase(&base); + not_null nnDerived(&derived); + not_null nnUnrelated(&unrelated); + + maybe_null_ret mnBase_ret1(&base), mnBase_ret2; + mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret + mnBase_ret2 = nnBase; // maybe_null_ret = not_null + + maybe_null_ret mnDerived_ret(&derived); + mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret + mnBase_ret1 = &derived; // maybe_null_ret = U; + mnBase_ret1 = nnDerived; // maybe_null_ret = not_null + + maybe_null_ret mnUnrelated_ret; + mnUnrelated_ret = &unrelated; // maybe_null_ret = T + + maybe_null_dbg mnBase_dbg1(&base), mnBase_dbg2; + mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg + mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null + + maybe_null_dbg mnDerived_dbg(&derived); + CHECK(mnDerived_dbg.present()); + mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg + + mnBase_dbg1 = &derived; // maybe_null_dbg = U; + mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null + + maybe_null_dbg mnUnrelated_dbg; + mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T + } } int main(int, const char *[]) diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index 7232840..46011b6 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -65,12 +65,19 @@ SUITE(NotNullTests) TEST(TestNotNullCasting) { - MyDerived derived; + MyBase base; + MyDerived derived; + Unrelated unrelated; + not_null u = &unrelated; not_null p = &derived; - not_null q = p; + not_null q = &base; + q = p; // allowed with heterogeneous copy ctor CHECK(q == p); #ifdef CONFIRM_COMPILATION_ERRORS + q = u; // no viable conversion possible between MyBase* and Unrelated* + p = q; // not possible to implicitly convert MyBase* to MyDerived* + not_null r = p; not_null s = reinterpret_cast(p); #endif -- cgit v1.2.3 From 4e596761eb0ae42ff032c44c22dba9be5adc8733 Mon Sep 17 00:00:00 2001 From: Gabriel Dos Reis Date: Mon, 28 Sep 2015 04:43:50 -0700 Subject: Update list of known platforms where GSL was successfully tested --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a32e89f..7d96dec 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ The test suite that exercises GSL has been built and passes successfully on the * GNU/Linux using GCC 5.1 * OS X Yosemite using Xcode with AppleClang 7.0.0.7000072 * OS X Yosemite using GCC-5.2.0 +* FreeBSD 10.x with Clang/LLVM 3.6 > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider contributing any changes that were necessary back to this project to benefit the wider community. -- cgit v1.2.3 From 6554e83c79e680500691cb2b3411c3c14d82623f Mon Sep 17 00:00:00 2001 From: Gabriel Dos Reis Date: Mon, 28 Sep 2015 05:10:44 -0700 Subject: Macro expand `constexpr` to nothing under MSVC Replace `_CONSTEXPR` with plain `constexpr`. --- include/array_view.h | 334 ++++++++++++++++++++++++++------------------------- 1 file changed, 169 insertions(+), 165 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 038b6e4..8bfdc69 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -30,10 +30,9 @@ #include #include "fail_fast.h" -#ifndef _MSC_VER -#define _CONSTEXPR constexpr -#else -#define _CONSTEXPR +#if defined(_MSC_VER) +#pragma push_macro("constexpr") +#define constexpr /* nothing */ #endif #pragma push_macro("_NOEXCEPT") @@ -88,24 +87,24 @@ namespace details using const_reference = const ValueType&; using value_type = ValueType; static const size_t rank = Rank; - _CONSTEXPR coordinate_facade() _NOEXCEPT + constexpr coordinate_facade() _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); } - _CONSTEXPR coordinate_facade(const value_type(&values)[rank]) _NOEXCEPT + constexpr coordinate_facade(const value_type(&values)[rank]) _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); for (size_t i = 0; i < rank; ++i) elems[i] = values[i]; } - _CONSTEXPR coordinate_facade(value_type e0) _NOEXCEPT + constexpr coordinate_facade(value_type e0) _NOEXCEPT { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); static_assert(rank == 1, "This constructor can only be used with rank == 1."); elems[0] = e0; } // Preconditions: il.size() == rank - _CONSTEXPR coordinate_facade(std::initializer_list il) + constexpr coordinate_facade(std::initializer_list il) { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); @@ -115,10 +114,10 @@ namespace details } } - _CONSTEXPR coordinate_facade(const coordinate_facade & other) = default; + constexpr coordinate_facade(const coordinate_facade & other) = default; template - _CONSTEXPR coordinate_facade(const coordinate_facade & other) + constexpr coordinate_facade(const coordinate_facade & other) { for (size_t i = 0; i < rank; ++i) { @@ -129,18 +128,18 @@ namespace details protected: coordinate_facade& operator=(const coordinate_facade& rhs) = default; // Preconditions: component_idx < rank - _CONSTEXPR reference operator[](size_t component_idx) + constexpr reference operator[](size_t component_idx) { fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } // Preconditions: component_idx < rank - _CONSTEXPR const_reference operator[](size_t component_idx) const + constexpr const_reference operator[](size_t component_idx) const { fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } - _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT + constexpr bool operator==(const ConcreteType& rhs) const _NOEXCEPT { for (size_t i = 0; i < rank; ++i) { @@ -149,94 +148,94 @@ namespace details } return true; } - _CONSTEXPR bool operator!=(const ConcreteType& rhs) const _NOEXCEPT + constexpr bool operator!=(const ConcreteType& rhs) const _NOEXCEPT { return !(to_concrete() == rhs); } - _CONSTEXPR ConcreteType operator+() const _NOEXCEPT + constexpr ConcreteType operator+() const _NOEXCEPT { return to_concrete(); } - _CONSTEXPR ConcreteType operator-() const + constexpr ConcreteType operator-() const { ConcreteType ret = to_concrete(); for (size_t i = 0; i < rank; ++i) ret.elems[i] = -ret.elems[i]; return ret; } - _CONSTEXPR ConcreteType operator+(const ConcreteType& rhs) const + constexpr ConcreteType operator+(const ConcreteType& rhs) const { ConcreteType ret = to_concrete(); ret += rhs; return ret; } - _CONSTEXPR ConcreteType operator-(const ConcreteType& rhs) const + constexpr ConcreteType operator-(const ConcreteType& rhs) const { ConcreteType ret = to_concrete(); ret -= rhs; return ret; } - _CONSTEXPR ConcreteType& operator+=(const ConcreteType& rhs) + constexpr ConcreteType& operator+=(const ConcreteType& rhs) { for (size_t i = 0; i < rank; ++i) elems[i] += rhs.elems[i]; return to_concrete(); } - _CONSTEXPR ConcreteType& operator-=(const ConcreteType& rhs) + constexpr ConcreteType& operator-=(const ConcreteType& rhs) { for (size_t i = 0; i < rank; ++i) elems[i] -= rhs.elems[i]; return to_concrete(); } - _CONSTEXPR ConcreteType& operator++() + constexpr ConcreteType& operator++() { static_assert(rank == 1, "This operator can only be used with rank == 1."); ++elems[0]; return to_concrete(); } - _CONSTEXPR ConcreteType operator++(int) + constexpr ConcreteType operator++(int) { static_assert(rank == 1, "This operator can only be used with rank == 1."); ConcreteType ret = to_concrete(); ++(*this); return ret; } - _CONSTEXPR ConcreteType& operator--() + constexpr ConcreteType& operator--() { static_assert(rank == 1, "This operator can only be used with rank == 1."); --elems[0]; return to_concrete(); } - _CONSTEXPR ConcreteType operator--(int) + constexpr ConcreteType operator--(int) { static_assert(rank == 1, "This operator can only be used with rank == 1."); ConcreteType ret = to_concrete(); --(*this); return ret; } - _CONSTEXPR ConcreteType operator*(value_type v) const + constexpr ConcreteType operator*(value_type v) const { ConcreteType ret = to_concrete(); ret *= v; return ret; } - _CONSTEXPR ConcreteType operator/(value_type v) const + constexpr ConcreteType operator/(value_type v) const { ConcreteType ret = to_concrete(); ret /= v; return ret; } - friend _CONSTEXPR ConcreteType operator*(value_type v, const ConcreteType& rhs) + friend constexpr ConcreteType operator*(value_type v, const ConcreteType& rhs) { return rhs * v; } - _CONSTEXPR ConcreteType& operator*=(value_type v) + constexpr ConcreteType& operator*=(value_type v) { for (size_t i = 0; i < rank; ++i) elems[i] *= v; return to_concrete(); } - _CONSTEXPR ConcreteType& operator/=(value_type v) + constexpr ConcreteType& operator/=(value_type v) { for (size_t i = 0; i < rank; ++i) elems[i] /= v; @@ -244,11 +243,11 @@ namespace details } value_type elems[rank] = {}; private: - _CONSTEXPR const ConcreteType& to_concrete() const _NOEXCEPT + constexpr const ConcreteType& to_concrete() const _NOEXCEPT { return static_cast(*this); } - _CONSTEXPR ConcreteType& to_concrete() _NOEXCEPT + constexpr ConcreteType& to_concrete() _NOEXCEPT { return static_cast(*this); } @@ -286,17 +285,17 @@ public: using const_reference = typename Base::const_reference; using size_type = typename Base::value_type; using value_type = typename Base::value_type; - _CONSTEXPR index() _NOEXCEPT : Base(){} - _CONSTEXPR index(const value_type (&values)[rank]) _NOEXCEPT : Base(values) {} - _CONSTEXPR index(std::initializer_list il) : Base(il) {} + constexpr index() _NOEXCEPT : Base(){} + constexpr index(const value_type (&values)[rank]) _NOEXCEPT : Base(values) {} + constexpr index(std::initializer_list il) : Base(il) {} - _CONSTEXPR index(const index &) = default; + constexpr index(const index &) = default; template - _CONSTEXPR index(const index &other) : Base(other) + constexpr index(const index &other) : Base(other) { } - _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT + constexpr static index shift_left(const index& other) _NOEXCEPT { value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); return index(arr); @@ -329,124 +328,124 @@ public: using size_type = ValueType; using value_type = ValueType; - _CONSTEXPR index() _NOEXCEPT : value(0) + constexpr index() _NOEXCEPT : value(0) { } - _CONSTEXPR index(value_type e0) _NOEXCEPT : value(e0) + constexpr index(value_type e0) _NOEXCEPT : value(e0) { } - _CONSTEXPR index(const value_type(&values)[1]) _NOEXCEPT : index(values[0]) + constexpr index(const value_type(&values)[1]) _NOEXCEPT : index(values[0]) { } // Preconditions: il.size() == rank - _CONSTEXPR index(std::initializer_list il) + constexpr index(std::initializer_list il) { fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); value = begin(il)[0]; } - _CONSTEXPR index(const index &) = default; + constexpr index(const index &) = default; template - _CONSTEXPR index(const index<1, OtherValueType> & other) + constexpr index(const index<1, OtherValueType> & other) { fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); value = static_cast(other.value); } - _CONSTEXPR static index shift_left(const index& other) _NOEXCEPT + constexpr static index shift_left(const index& other) _NOEXCEPT { return other.elems[1]; } // Preconditions: component_idx < rank - _CONSTEXPR reference operator[](size_type component_idx) _NOEXCEPT + constexpr reference operator[](size_type component_idx) _NOEXCEPT { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } // Preconditions: component_idx < rank - _CONSTEXPR const_reference operator[](size_type component_idx) const _NOEXCEPT + constexpr const_reference operator[](size_type component_idx) const _NOEXCEPT { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } - _CONSTEXPR bool operator==(const index& rhs) const _NOEXCEPT + constexpr bool operator==(const index& rhs) const _NOEXCEPT { return value == rhs.value; } - _CONSTEXPR bool operator!=(const index& rhs) const _NOEXCEPT + constexpr bool operator!=(const index& rhs) const _NOEXCEPT { return !(*this == rhs); } - _CONSTEXPR index operator+() const _NOEXCEPT + constexpr index operator+() const _NOEXCEPT { return *this; } - _CONSTEXPR index operator-() const _NOEXCEPT + constexpr index operator-() const _NOEXCEPT { return index(-value); } - _CONSTEXPR index operator+(const index& rhs) const _NOEXCEPT + constexpr index operator+(const index& rhs) const _NOEXCEPT { return index(value + rhs.value); } - _CONSTEXPR index operator-(const index& rhs) const _NOEXCEPT + constexpr index operator-(const index& rhs) const _NOEXCEPT { return index(value - rhs.value); } - _CONSTEXPR index& operator+=(const index& rhs) _NOEXCEPT + constexpr index& operator+=(const index& rhs) _NOEXCEPT { value += rhs.value; return *this; } - _CONSTEXPR index& operator-=(const index& rhs) _NOEXCEPT + constexpr index& operator-=(const index& rhs) _NOEXCEPT { value -= rhs.value; return *this; } - _CONSTEXPR index& operator++() _NOEXCEPT + constexpr index& operator++() _NOEXCEPT { ++value; return *this; } - _CONSTEXPR index operator++(int) _NOEXCEPT + constexpr index operator++(int) _NOEXCEPT { index ret = *this; ++(*this); return ret; } - _CONSTEXPR index& operator--() _NOEXCEPT + constexpr index& operator--() _NOEXCEPT { --value; return *this; } - _CONSTEXPR index operator--(int) _NOEXCEPT + constexpr index operator--(int) _NOEXCEPT { index ret = *this; --(*this); return ret; } - _CONSTEXPR index operator*(value_type v) const _NOEXCEPT + constexpr index operator*(value_type v) const _NOEXCEPT { return index(value * v); } - _CONSTEXPR index operator/(value_type v) const _NOEXCEPT + constexpr index operator/(value_type v) const _NOEXCEPT { return index(value / v); } - _CONSTEXPR index& operator*=(value_type v) _NOEXCEPT + constexpr index& operator*=(value_type v) _NOEXCEPT { value *= v; return *this; } - _CONSTEXPR index& operator/=(value_type v) _NOEXCEPT + constexpr index& operator/=(value_type v) _NOEXCEPT { value /= v; return *this; } - friend _CONSTEXPR index operator*(value_type v, const index& rhs) _NOEXCEPT + friend constexpr index operator*(value_type v, const index& rhs) _NOEXCEPT { return index(rhs * v); } @@ -777,7 +776,7 @@ class static_bounds && details::SizeTypeTraits::max_value <= SIZE_MAX, "SizeType must be an integral type and its numeric limits must be smaller than SIZE_MAX"); MyRanges m_ranges; - _CONSTEXPR static_bounds(const MyRanges & range) : m_ranges(range) { } + constexpr static_bounds(const MyRanges & range) : m_ranges(range) { } template friend class static_bounds; @@ -794,72 +793,72 @@ public: using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; public: - _CONSTEXPR static_bounds(const static_bounds &) = default; + constexpr static_bounds(const static_bounds &) = default; template , details::BoundsRanges >::value>> - _CONSTEXPR static_bounds(const static_bounds &other): + constexpr static_bounds(const static_bounds &other): m_ranges(other.m_ranges) { } - _CONSTEXPR static_bounds(std::initializer_list il) : m_ranges(il.begin()) + constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); fail_fast_assert(m_ranges.totalSize() <= details::SizeTypeTraits::max_value, "Size of the range is larger than the max element of the size type"); } - _CONSTEXPR static_bounds() = default; + constexpr static_bounds() = default; - _CONSTEXPR static_bounds & operator = (const static_bounds & otherBounds) + constexpr static_bounds & operator = (const static_bounds & otherBounds) { new(&m_ranges) MyRanges (otherBounds.m_ranges); return *this; } - _CONSTEXPR sliced_type slice() const _NOEXCEPT + constexpr sliced_type slice() const _NOEXCEPT { return sliced_type{static_cast &>(m_ranges)}; } - _CONSTEXPR size_type stride() const _NOEXCEPT + constexpr size_type stride() const _NOEXCEPT { return rank > 1 ? slice().size() : 1; } - _CONSTEXPR size_type size() const _NOEXCEPT + constexpr size_type size() const _NOEXCEPT { return static_cast(m_ranges.totalSize()); } - _CONSTEXPR size_type total_size() const _NOEXCEPT + constexpr size_type total_size() const _NOEXCEPT { return static_cast(m_ranges.totalSize()); } - _CONSTEXPR size_type linearize(const index_type & idx) const + constexpr size_type linearize(const index_type & idx) const { return m_ranges.linearize(idx); } - _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + constexpr bool contains(const index_type& idx) const _NOEXCEPT { return m_ranges.contains(idx) != -1; } - _CONSTEXPR size_type operator[](size_t index) const _NOEXCEPT + constexpr size_type operator[](size_t index) const _NOEXCEPT { return m_ranges.elementNum(index); } template - _CONSTEXPR size_type extent() const _NOEXCEPT + constexpr size_type extent() const _NOEXCEPT { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return details::createTypeListIndexer(m_ranges).template get().elementNum(); } - _CONSTEXPR index_type index_bounds() const _NOEXCEPT + constexpr index_type index_bounds() const _NOEXCEPT { index_type extents; m_ranges.serialize(extents); @@ -867,23 +866,23 @@ public: } template - _CONSTEXPR bool operator == (const static_bounds & rhs) const _NOEXCEPT + constexpr bool operator == (const static_bounds & rhs) const _NOEXCEPT { return this->size() == rhs.size(); } template - _CONSTEXPR bool operator != (const static_bounds & rhs) const _NOEXCEPT + constexpr bool operator != (const static_bounds & rhs) const _NOEXCEPT { return !(*this == rhs); } - _CONSTEXPR const_iterator begin() const _NOEXCEPT + constexpr const_iterator begin() const _NOEXCEPT { return const_iterator(*this); } - _CONSTEXPR const_iterator end() const _NOEXCEPT + constexpr const_iterator end() const _NOEXCEPT { index_type boundary; m_ranges.serialize(boundary); @@ -913,43 +912,43 @@ public: static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - _CONSTEXPR strided_bounds(const strided_bounds &) = default; + constexpr strided_bounds(const strided_bounds &) = default; template - _CONSTEXPR strided_bounds(const strided_bounds &other) + constexpr strided_bounds(const strided_bounds &other) : Base(other), m_strides(other.strides) { } - _CONSTEXPR strided_bounds(const index_type &extents, const index_type &strides) + constexpr strided_bounds(const index_type &extents, const index_type &strides) : m_strides(strides) { for (size_t i = 0; i < rank; i++) Base::elems[i] = extents[i]; } - _CONSTEXPR strided_bounds(const value_type(&values)[rank], index_type strides) + constexpr strided_bounds(const value_type(&values)[rank], index_type strides) : Base(values), m_strides(std::move(strides)) { } - _CONSTEXPR index_type strides() const _NOEXCEPT + constexpr index_type strides() const _NOEXCEPT { return m_strides; } - _CONSTEXPR size_type total_size() const _NOEXCEPT + constexpr size_type total_size() const _NOEXCEPT { size_type ret = 0; for (size_t i = 0; i < rank; ++i) ret += (Base::elems[i] - 1) * m_strides[i]; return ret + 1; } - _CONSTEXPR size_type size() const _NOEXCEPT + constexpr size_type size() const _NOEXCEPT { size_type ret = 1; for (size_t i = 0; i < rank; ++i) ret *= Base::elems[i]; return ret; } - _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + constexpr bool contains(const index_type& idx) const _NOEXCEPT { for (size_t i = 0; i < rank; ++i) { @@ -958,7 +957,7 @@ public: } return true; } - _CONSTEXPR size_type linearize(const index_type & idx) const + constexpr size_type linearize(const index_type & idx) const { size_type ret = 0; for (size_t i = 0; i < rank; i++) @@ -968,22 +967,22 @@ public: } return ret; } - _CONSTEXPR size_type stride() const _NOEXCEPT + constexpr size_type stride() const _NOEXCEPT { return m_strides[0]; } template 1), typename Ret = std::enable_if_t> - _CONSTEXPR sliced_type slice() const + constexpr sliced_type slice() const { return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; } template - _CONSTEXPR size_type extent() const _NOEXCEPT + constexpr size_type extent() const _NOEXCEPT { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); return Base::elems[Dim]; } - _CONSTEXPR index_type index_bounds() const _NOEXCEPT + constexpr index_type index_bounds() const _NOEXCEPT { return index_type(Base::elems); } @@ -1323,14 +1322,14 @@ bounds_iterator operator+(typename bounds_iterator::differ namespace details { template - _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT { return bnd.strides(); } // Make a stride vector from bounds, assuming contiguous memory. template - _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT { auto extents = bnd.index_bounds(); typename Bounds::index_type stride; @@ -1380,30 +1379,30 @@ private: bounds_type m_bounds; public: - _CONSTEXPR bounds_type bounds() const _NOEXCEPT + constexpr bounds_type bounds() const _NOEXCEPT { return m_bounds; } template - _CONSTEXPR size_type extent() const _NOEXCEPT + constexpr size_type extent() const _NOEXCEPT { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return m_bounds.template extent(); } - _CONSTEXPR size_type size() const _NOEXCEPT + constexpr size_type size() const _NOEXCEPT { return m_bounds.size(); } - _CONSTEXPR reference operator[](const index_type& idx) const + constexpr reference operator[](const index_type& idx) const { return m_pdata[m_bounds.linearize(idx)]; } - _CONSTEXPR pointer data() const _NOEXCEPT + constexpr pointer data() const _NOEXCEPT { return m_pdata; } template 1), typename Ret = std::enable_if_t> - _CONSTEXPR Ret operator[](size_type idx) const + constexpr Ret operator[](size_type idx) const { fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); const size_type ridx = idx * m_bounds.stride(); @@ -1412,78 +1411,78 @@ public: return Ret {m_pdata + ridx, m_bounds.slice()}; } - _CONSTEXPR operator bool () const _NOEXCEPT + constexpr operator bool () const _NOEXCEPT { return m_pdata != nullptr; } - _CONSTEXPR iterator begin() const + constexpr iterator begin() const { return iterator {this, true}; } - _CONSTEXPR iterator end() const + constexpr iterator end() const { return iterator {this}; } - _CONSTEXPR const_iterator cbegin() const + constexpr const_iterator cbegin() const { return const_iterator {reinterpret_cast *>(this), true}; } - _CONSTEXPR const_iterator cend() const + constexpr const_iterator cend() const { return const_iterator {reinterpret_cast *>(this)}; } - _CONSTEXPR reverse_iterator rbegin() const + constexpr reverse_iterator rbegin() const { return reverse_iterator {end()}; } - _CONSTEXPR reverse_iterator rend() const + constexpr reverse_iterator rend() const { return reverse_iterator {begin()}; } - _CONSTEXPR const_reverse_iterator crbegin() const + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator {cend()}; } - _CONSTEXPR const_reverse_iterator crend() const + constexpr const_reverse_iterator crend() const { return const_reverse_iterator {cbegin()}; } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator== (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator== (const basic_array_view & other) const _NOEXCEPT { return m_bounds.size() == other.m_bounds.size() && (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator!= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator!= (const basic_array_view & other) const _NOEXCEPT { return !(*this == other); } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator< (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator< (const basic_array_view & other) const _NOEXCEPT { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator<= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator<= (const basic_array_view & other) const _NOEXCEPT { return !(other < *this); } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator> (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator> (const basic_array_view & other) const _NOEXCEPT { return (other < *this); } template , std::remove_cv_t>::value>> - _CONSTEXPR bool operator>= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator>= (const basic_array_view & other) const _NOEXCEPT { return !(*this < other); } @@ -1492,27 +1491,27 @@ public: template ::value && std::is_convertible::value>> - _CONSTEXPR basic_array_view(const basic_array_view & other ) _NOEXCEPT + constexpr basic_array_view(const basic_array_view & other ) _NOEXCEPT : m_pdata(other.m_pdata), m_bounds(other.m_bounds) { } protected: - _CONSTEXPR basic_array_view(pointer data, bounds_type bound) _NOEXCEPT + constexpr basic_array_view(pointer data, bounds_type bound) _NOEXCEPT : m_pdata(data) , m_bounds(std::move(bound)) { fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } template - _CONSTEXPR basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) _NOEXCEPT + constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) _NOEXCEPT : m_pdata(reinterpret_cast(data)) , m_bounds(std::move(bound)) { fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } template - _CONSTEXPR basic_array_view as_array_view(const DestBounds &bounds) + constexpr basic_array_view as_array_view(const DestBounds &bounds) { details::verifyBoundsReshape(m_bounds, bounds); return {m_pdata, bounds}; @@ -1661,22 +1660,22 @@ public: public: // basic - _CONSTEXPR array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + constexpr array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) { } - _CONSTEXPR array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) + constexpr array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) { } - _CONSTEXPR array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) { fail_fast_assert(size == 0); } // default template > - _CONSTEXPR array_view() : Base(nullptr, bounds_type()) + constexpr array_view() : Base(nullptr, bounds_type()) { } @@ -1684,7 +1683,7 @@ public: template , typename Dummy = std::enable_if_t::value && std::is_convertible::value>> - _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) + constexpr array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) { } @@ -1692,7 +1691,7 @@ public: template , typename Dummy = std::enable_if_t::value && std::is_convertible::value>> - _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) { } @@ -1700,19 +1699,19 @@ public: template , typename Dummy = std::enable_if_t::value && std::is_convertible::value >> - _CONSTEXPR array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) + constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) { fail_fast_assert(size <= N); } // from std array template , typename Base::bounds_type>::value>> - _CONSTEXPR array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) + constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) { } template , typename Base::bounds_type>::value && std::is_const::value>> - _CONSTEXPR array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) { } @@ -1721,7 +1720,7 @@ public: template ::value && details::LessThan::value>> // remove literal 0 case - _CONSTEXPR array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + constexpr array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) { } @@ -1732,12 +1731,12 @@ public: && std::is_convertible, typename Base::bounds_type>::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > - _CONSTEXPR array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + constexpr array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) { } - _CONSTEXPR array_view(const array_view &) = default; + constexpr array_view(const array_view &) = default; // convertible template ::value_type, static_bounds::size_type, OtherDimensions...>>, typename Dummy = std::enable_if_t::value> > - _CONSTEXPR array_view(const array_view &av) : Base(static_cast::Base &>(av)) {} // static_cast is required + constexpr array_view(const array_view &av) : Base(static_cast::Base &>(av)) {} // static_cast is required // reshape template - _CONSTEXPR array_view as_array_view(Dimensions2... dims) + constexpr array_view as_array_view(Dimensions2... dims) { static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); using BoundsType = typename array_view::bounds_type; @@ -1760,7 +1759,7 @@ public: // to bytes array template ::value_type>>::value> - _CONSTEXPR auto as_bytes() const _NOEXCEPT -> + constexpr auto as_bytes() const _NOEXCEPT -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); @@ -1768,7 +1767,7 @@ public: } template ::value_type>>::value> - _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT -> + constexpr auto as_writeable_bytes() const _NOEXCEPT -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); @@ -1778,7 +1777,7 @@ public: // from bytes array template::value, typename Dummy = std::enable_if_t> - _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + constexpr auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1787,7 +1786,7 @@ public: } template::value, typename Dummy = std::enable_if_t> - _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + constexpr auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1797,79 +1796,79 @@ public: // section on linear space template - _CONSTEXPR array_view first() const _NOEXCEPT + constexpr array_view first() const _NOEXCEPT { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed return { this->data(), Count }; } - _CONSTEXPR array_view first(size_type count) const _NOEXCEPT + constexpr array_view first(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); return { this->data(), count }; } template - _CONSTEXPR array_view last() const _NOEXCEPT + constexpr array_view last() const _NOEXCEPT { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data() + this->size() - Count, Count }; } - _CONSTEXPR array_view last(size_type count) const _NOEXCEPT + constexpr array_view last(size_type count) const _NOEXCEPT { fail_fast_assert(count <= this->size()); return { this->data() + this->size() - count, count }; } template - _CONSTEXPR array_view sub() const _NOEXCEPT + constexpr array_view sub() const _NOEXCEPT { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } - _CONSTEXPR array_view sub(size_type offset, size_type count = dynamic_range) const _NOEXCEPT + constexpr array_view sub(size_type offset, size_type count = dynamic_range) const _NOEXCEPT { fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; } // size - _CONSTEXPR size_type length() const _NOEXCEPT + constexpr size_type length() const _NOEXCEPT { return this->size(); } - _CONSTEXPR size_type used_length() const _NOEXCEPT + constexpr size_type used_length() const _NOEXCEPT { return length(); } - _CONSTEXPR size_type bytes() const _NOEXCEPT + constexpr size_type bytes() const _NOEXCEPT { return sizeof(value_type) * this->size(); } - _CONSTEXPR size_type used_bytes() const _NOEXCEPT + constexpr size_type used_bytes() const _NOEXCEPT { return bytes(); } // section - _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const + constexpr strided_array_view section(index_type origin, index_type extents) const { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; } - _CONSTEXPR reference operator[](const index_type& idx) const + constexpr reference operator[](const index_type& idx) const { return Base::operator[](idx); } template 1), typename Dummy = std::enable_if_t> - _CONSTEXPR array_view operator[](size_type idx) const + constexpr array_view operator[](size_type idx) const { auto ret = Base::operator[](idx); return{ ret.data(), ret.bounds() }; @@ -1884,53 +1883,53 @@ public: }; template -_CONSTEXPR auto as_array_view(T * const & ptr, dim... args) -> array_view, Dimensions...> +constexpr auto as_array_view(T * const & ptr, dim... args) -> array_view, Dimensions...> { return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; } template -_CONSTEXPR auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits::type { return {arr, len}; } template -_CONSTEXPR auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type { return {arr}; } template -_CONSTEXPR array_view as_array_view(const std::array &arr) +constexpr array_view as_array_view(const std::array &arr) { return {arr}; } template -_CONSTEXPR array_view as_array_view(const std::array &&) = delete; +constexpr array_view as_array_view(const std::array &&) = delete; template -_CONSTEXPR array_view as_array_view(std::array &arr) +constexpr array_view as_array_view(std::array &arr) { return {arr}; } template -_CONSTEXPR array_view as_array_view(T *begin, T *end) +constexpr array_view as_array_view(T *begin, T *end) { return {begin, end}; } template -_CONSTEXPR auto as_array_view(Cont &arr) -> std::enable_if_t>::value, +constexpr auto as_array_view(Cont &arr) -> std::enable_if_t>::value, array_view, dynamic_range>> { return {arr.data(), arr.size()}; } template -_CONSTEXPR auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, +constexpr auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, array_view, dynamic_range>> = delete; template @@ -1977,7 +1976,7 @@ public: typename OtherBaseType = basic_array_view::value_type, strided_bounds::size_type>>, typename Dummy = std::enable_if_t::value> > - _CONSTEXPR strided_array_view(const strided_array_view &av): Base(static_cast::Base &>(av)) // static_cast is required + constexpr strided_array_view(const strided_array_view &av): Base(static_cast::Base &>(av)) // static_cast is required { } @@ -1998,13 +1997,13 @@ public: return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; } - _CONSTEXPR reference operator[](const index_type& idx) const + constexpr reference operator[](const index_type& idx) const { return Base::operator[](idx); } template 1), typename Dummy = std::enable_if_t> - _CONSTEXPR strided_array_view operator[](size_type idx) const + constexpr strided_array_view operator[](size_type idx) const { auto ret = Base::operator[](idx); return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; @@ -2282,6 +2281,11 @@ general_array_view_iterator operator+(typename general_array_view_ite } // namespace Guide +#if defined(_MSC_VER) +#undef constexpr +#pragma pop_macro("constexpr") +#endif + #if defined(_MSC_VER) && _MSC_VER <= 1800 #pragma warning(pop) #endif // _MSC_VER <= 1800 -- cgit v1.2.3 From 3402b92ef67571688e927b96c67f2ed7b79e97dc Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Mon, 28 Sep 2015 21:20:02 +0300 Subject: fix clang -Wunused-parameter warnings --- include/array_view.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 8bfdc69..ca4f897 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -548,7 +548,7 @@ namespace details // TODO : following signature is for work around VS bug template - BoundsRanges (const OtherType &, bool firstLevel) {} + BoundsRanges (const OtherType &, bool /* firstLevel */) {} BoundsRanges(const SizeType * const) { } BoundsRanges() = default; @@ -593,7 +593,7 @@ namespace details BoundsRanges() : m_bound(0) {} template - BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : + BoundsRanges(const BoundsRanges &other, bool /* firstLevel */ = true) : Base(static_cast&>(other), false), m_bound (static_cast(other.totalSize())) { } @@ -764,7 +764,7 @@ class bounds_iterator; template class static_bounds { public: - static_bounds(const details::BoundsRanges &empty) { + static_bounds(const details::BoundsRanges &) { } }; @@ -2021,7 +2021,7 @@ private: } template > - static index_type resize_stride(const index_type& strides, size_t d, void *p = 0) + static index_type resize_stride(const index_type& strides, size_t , void * = 0) { fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); -- cgit v1.2.3 From e46160c18ec633ee7f20d1d32df85c783bba18f6 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Mon, 28 Sep 2015 23:20:28 +0300 Subject: it's better to pass a small (e.g. 16 bytes) object by value then by const reference --- include/string_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_view.h b/include/string_view.h index e683c7a..164c3ac 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -137,7 +137,7 @@ basic_string_view::type, dy // to_string() allow (explicit) conversions from string_view to string // template -std::basic_string::type> to_string(const basic_string_view& view) +std::basic_string::type> to_string(basic_string_view view) { return{ view.data(), view.length() }; } -- cgit v1.2.3 From a46d6fcf0d69a20f630cd590ba0867a1cf6a8cb4 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 28 Sep 2015 15:17:37 -0700 Subject: Fix issue #49 --- include/gsl.h | 4 +- tests/maybenull_tests.cpp | 94 +++++++++++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 1405993..a73db6b 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -223,12 +223,12 @@ public: template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) {} + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.ptr_), tested_(false) {} template ::value>> maybe_null_dbg& operator=(const maybe_null_dbg &other) { - ptr_ = other.get(); + ptr_ = other.ptr_; tested_ = false; return *this; } diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index d6b53f0..a889e52 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -256,42 +256,64 @@ SUITE(MaybeNullTests) CHECK(p1.get() != nullptr); } - TEST(TestMaybeNullAssignmentOps) - { - MyBase base; - MyDerived derived; - Unrelated unrelated; - - not_null nnBase(&base); - not_null nnDerived(&derived); - not_null nnUnrelated(&unrelated); - - maybe_null_ret mnBase_ret1(&base), mnBase_ret2; - mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret - mnBase_ret2 = nnBase; // maybe_null_ret = not_null - - maybe_null_ret mnDerived_ret(&derived); - mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret - mnBase_ret1 = &derived; // maybe_null_ret = U; - mnBase_ret1 = nnDerived; // maybe_null_ret = not_null - - maybe_null_ret mnUnrelated_ret; - mnUnrelated_ret = &unrelated; // maybe_null_ret = T - - maybe_null_dbg mnBase_dbg1(&base), mnBase_dbg2; - mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null - - maybe_null_dbg mnDerived_dbg(&derived); - CHECK(mnDerived_dbg.present()); - mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg - - mnBase_dbg1 = &derived; // maybe_null_dbg = U; - mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null - - maybe_null_dbg mnUnrelated_dbg; - mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T - } + template + void TestMaybeNullAssignmentOpsHelper() + { + MyBase base; + MyDerived derived; + Unrelated unrelated; + + not_null nnBase(&base); + not_null nnDerived(&derived); + not_null nnUnrelated(&unrelated); + + maybe_null_type::type mnBase_ret1(&base), mnBase_ret2; + mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret + mnBase_ret2 = nnBase; // maybe_null_ret = not_null + + maybe_null_type::type mnDerived_ret(&derived); + mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret + mnBase_ret1 = &derived; // maybe_null_ret = U; + mnBase_ret1 = nnDerived; // maybe_null_ret = not_null + + maybe_null_type::type mnUnrelated_ret; + mnUnrelated_ret = &unrelated; // maybe_null_ret = T + + maybe_null_type::type mnBase_dbg1(&base), mnBase_dbg2; + mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg + mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null + + maybe_null_type::type mnDerived_dbg(&derived); + CHECK(mnDerived_dbg.present()); + mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg + + mnBase_dbg1 = &derived; // maybe_null_dbg = U; + mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null + + maybe_null_type::type mnUnrelated_dbg; + mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T + } + + struct maybe_null_ret_type + { + template + using type = maybe_null_ret; + }; + struct maybe_null_dbg_type + { + template + using type = maybe_null_dbg; + }; + + TEST(TestMaybeNullRetAssignmentOps) + { + TestMaybeNullAssignmentOpsHelper(); + } + + TEST(TestMaybeNullDbgAssignmentOps) + { + TestMaybeNullAssignmentOpsHelper(); + } } int main(int, const char *[]) -- cgit v1.2.3 From b29566628e180ce100f9cbdb08d4ba56ab650a01 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 28 Sep 2015 18:26:35 -0700 Subject: Revert "Fix issue #49" This reverts commit a46d6fcf0d69a20f630cd590ba0867a1cf6a8cb4. --- include/gsl.h | 4 +- tests/maybenull_tests.cpp | 94 ++++++++++++++++++----------------------------- 2 files changed, 38 insertions(+), 60 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index a73db6b..1405993 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -223,12 +223,12 @@ public: template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.ptr_), tested_(false) {} + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) {} template ::value>> maybe_null_dbg& operator=(const maybe_null_dbg &other) { - ptr_ = other.ptr_; + ptr_ = other.get(); tested_ = false; return *this; } diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index a889e52..d6b53f0 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -256,64 +256,42 @@ SUITE(MaybeNullTests) CHECK(p1.get() != nullptr); } - template - void TestMaybeNullAssignmentOpsHelper() - { - MyBase base; - MyDerived derived; - Unrelated unrelated; - - not_null nnBase(&base); - not_null nnDerived(&derived); - not_null nnUnrelated(&unrelated); - - maybe_null_type::type mnBase_ret1(&base), mnBase_ret2; - mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret - mnBase_ret2 = nnBase; // maybe_null_ret = not_null - - maybe_null_type::type mnDerived_ret(&derived); - mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret - mnBase_ret1 = &derived; // maybe_null_ret = U; - mnBase_ret1 = nnDerived; // maybe_null_ret = not_null - - maybe_null_type::type mnUnrelated_ret; - mnUnrelated_ret = &unrelated; // maybe_null_ret = T - - maybe_null_type::type mnBase_dbg1(&base), mnBase_dbg2; - mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null - - maybe_null_type::type mnDerived_dbg(&derived); - CHECK(mnDerived_dbg.present()); - mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg - - mnBase_dbg1 = &derived; // maybe_null_dbg = U; - mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null - - maybe_null_type::type mnUnrelated_dbg; - mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T - } - - struct maybe_null_ret_type - { - template - using type = maybe_null_ret; - }; - struct maybe_null_dbg_type - { - template - using type = maybe_null_dbg; - }; - - TEST(TestMaybeNullRetAssignmentOps) - { - TestMaybeNullAssignmentOpsHelper(); - } - - TEST(TestMaybeNullDbgAssignmentOps) - { - TestMaybeNullAssignmentOpsHelper(); - } + TEST(TestMaybeNullAssignmentOps) + { + MyBase base; + MyDerived derived; + Unrelated unrelated; + + not_null nnBase(&base); + not_null nnDerived(&derived); + not_null nnUnrelated(&unrelated); + + maybe_null_ret mnBase_ret1(&base), mnBase_ret2; + mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret + mnBase_ret2 = nnBase; // maybe_null_ret = not_null + + maybe_null_ret mnDerived_ret(&derived); + mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret + mnBase_ret1 = &derived; // maybe_null_ret = U; + mnBase_ret1 = nnDerived; // maybe_null_ret = not_null + + maybe_null_ret mnUnrelated_ret; + mnUnrelated_ret = &unrelated; // maybe_null_ret = T + + maybe_null_dbg mnBase_dbg1(&base), mnBase_dbg2; + mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg + mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null + + maybe_null_dbg mnDerived_dbg(&derived); + CHECK(mnDerived_dbg.present()); + mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg + + mnBase_dbg1 = &derived; // maybe_null_dbg = U; + mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null + + maybe_null_dbg mnUnrelated_dbg; + mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T + } } int main(int, const char *[]) -- cgit v1.2.3 From 83333419de094f305f1bcaa1e12f7ed801c0cbfa Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 28 Sep 2015 18:34:04 -0700 Subject: Add test that reproduces issue --- tests/maybenull_tests.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index d6b53f0..944307e 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -178,13 +178,19 @@ SUITE(MaybeNullTests) maybe_null q = p; CHECK(q == p); + maybe_null_dbg pdbg = &derived; + CHECK(pdbg.present()); + + maybe_null_dbg qdbg = pdbg; + CHECK(qdbg == pdbg); + #ifdef CONFIRM_COMPILATION_ERRORS maybe_null r = p; maybe_null s = reinterpret_cast(p); #endif maybe_null_dbg t = reinterpret_cast(p.get()); - CHECK_THROW((void)(void*)t.get(), fail_fast); + CHECK_THROW((void)(void*)t.get(), fail_fast); maybe_null_dbg u = reinterpret_cast(p.get()); CHECK(u.present()); CHECK((void*)p.get() == (void*)u.get()); -- cgit v1.2.3 From 444bf9640a91c674a5812704e9e1159f0efbda1d Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 28 Sep 2015 18:49:53 -0700 Subject: Fix issue #49 --- include/gsl.h | 4 ++-- tests/maybenull_tests.cpp | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 1405993..a73db6b 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -223,12 +223,12 @@ public: template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) {} + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.ptr_), tested_(false) {} template ::value>> maybe_null_dbg& operator=(const maybe_null_dbg &other) { - ptr_ = other.get(); + ptr_ = other.ptr_; tested_ = false; return *this; } diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 944307e..661b26a 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -289,9 +289,7 @@ SUITE(MaybeNullTests) mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null maybe_null_dbg mnDerived_dbg(&derived); - CHECK(mnDerived_dbg.present()); mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg1 = &derived; // maybe_null_dbg = U; mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null -- cgit v1.2.3 From 422e7164d53627650d2772c08c27b6b65920bc49 Mon Sep 17 00:00:00 2001 From: john-lynch Date: Mon, 28 Sep 2015 23:40:25 -0500 Subject: Fixing move constructor of Final_act to take non-const r-value reference and move properly from other Final_act object so that correctness is not dependent on copy elison. --- include/gsl.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index a73db6b..6de3780 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -50,16 +50,17 @@ template class Final_act { public: - explicit Final_act(F f) : f_(std::move(f)) {} - - Final_act(const Final_act&& other) : f_(other.f_) {} + explicit Final_act(F f) : f_(std::move(f)), invoke_(true) {} + + Final_act(Final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other._invoke = false; } Final_act(const Final_act&) = delete; Final_act& operator=(const Final_act&) = delete; - - ~Final_act() { f_(); } + + ~Final_act() { if (invoke_) f_(); } private: F f_; + bool invoke_; }; // finally() - convenience function to generate a Final_act -- cgit v1.2.3 From 1d11cd1ed13593f5892aff847dfbb42b85a04037 Mon Sep 17 00:00:00 2001 From: john-lynch Date: Tue, 29 Sep 2015 00:00:21 -0500 Subject: Fixing typo in move constructor implementation that resulted in compile error during tests. --- include/gsl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gsl.h b/include/gsl.h index 6de3780..95116dc 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -52,7 +52,7 @@ class Final_act public: explicit Final_act(F f) : f_(std::move(f)), invoke_(true) {} - Final_act(Final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other._invoke = false; } + Final_act(Final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other.invoke_ = false; } Final_act(const Final_act&) = delete; Final_act& operator=(const Final_act&) = delete; -- cgit v1.2.3 From ae24c0fe06f92a2a77fb4dbd0098335519a54390 Mon Sep 17 00:00:00 2001 From: john-lynch Date: Tue, 29 Sep 2015 00:03:15 -0500 Subject: Added test that Final_act object can be moved properly. While this is not a common case, it may happen if the user calls finally and the compiler fails to perform RVO. --- tests/utils_tests.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 0dc4809..66b6d14 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -37,6 +37,20 @@ SUITE(utils_tests) CHECK(i == 1); } + TEST(finally_lambda_move) + { + int i = 0; + { + auto _1 = finally([&]() {f(i);}); + { + auto _2 = std::move(_1); + CHECK(i == 0); + } + CHECK(i == 1); + } + CHECK(i == 1); + } + TEST(finally_function_with_bind) { int i = 0; -- cgit v1.2.3 From ef626fd33a2f22ac917394995270da225dbc3c5f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 29 Sep 2015 16:41:37 -0700 Subject: Renamed namespace to 'gsl'. Renamed macro to configure testing. --- include/array_view.h | 8 ++++---- include/fail_fast.h | 6 +++--- include/gsl.h | 8 ++++---- include/string_view.h | 4 ++-- tests/CMakeLists.txt | 2 +- tests/array_view_tests.cpp | 6 +++--- tests/assertion_tests.cpp | 2 +- tests/at_tests.cpp | 2 +- tests/bounds_tests.cpp | 2 +- tests/maybenull_tests.cpp | 2 +- tests/notnull_tests.cpp | 2 +- tests/owner_tests.cpp | 2 +- tests/string_view_tests.cpp | 2 +- tests/utils_tests.cpp | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index ca4f897..9db9d53 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -39,7 +39,7 @@ #ifndef _NOEXCEPT -#ifdef SAFER_CPP_TESTING +#ifdef GSL_THROWS_FOR_TESTING #define _NOEXCEPT #else #define _NOEXCEPT noexcept @@ -47,7 +47,7 @@ #else // _NOEXCEPT -#ifdef SAFER_CPP_TESTING +#ifdef GSL_THROWS_FOR_TESTING #undef _NOEXCEPT #define _NOEXCEPT #endif @@ -59,7 +59,7 @@ #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior #endif // _MSC_VER <= 1800 -namespace Guide { +namespace gsl { /* ** begin definitions of index and bounds @@ -2279,7 +2279,7 @@ general_array_view_iterator operator+(typename general_array_view_ite return rhs + n; } -} // namespace Guide +} // namespace gsl #if defined(_MSC_VER) #undef constexpr diff --git a/include/fail_fast.h b/include/fail_fast.h index 78a5102..477bace 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -22,14 +22,14 @@ #include #include -namespace Guide +namespace gsl { // // Having "fail fast" result in an exception makes unit testing // the GSL classes that rely upon it much simpler. // -#if defined(SAFER_CPP_TESTING) +#if defined(GSL_THROWS_FOR_TESTING) struct fail_fast : public std::runtime_error { @@ -45,7 +45,7 @@ inline void fail_fast_assert(bool cond, const char* const message) { if (!cond) 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 // SAFER_CPP_TESTING +#endif // GSL_THROWS_FOR_TESTING } diff --git a/include/gsl.h b/include/gsl.h index 95116dc..5ae4df6 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -23,7 +23,7 @@ #include "string_view.h" // zstring, string_view, zstring_builder... #include -namespace Guide +namespace gsl { // @@ -38,8 +38,8 @@ using owner = T; // // GSL.assert: assertions // -#define Expects(x) Guide::fail_fast_assert((x)) -#define Ensures(x) Guide::fail_fast_assert((x)) +#define Expects(x) gsl::fail_fast_assert((x)) +#define Ensures(x) gsl::fail_fast_assert((x)) // // GSL.util: utilities @@ -354,6 +354,6 @@ private: template using maybe_null = maybe_null_ret; -} // namespace Guide +} // namespace gsl #endif // GSL_GSL_H diff --git a/include/string_view.h b/include/string_view.h index 164c3ac..4076d52 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -22,7 +22,7 @@ #include "array_view.h" #include -namespace Guide +namespace gsl { // // czstring and wzstring @@ -164,7 +164,7 @@ public: size_type length() const { return sv_.length(); } pointer assume0() const { return data(); } - string_view_type ensure_z() const { return Guide::ensure_z(sv_); } + string_view_type ensure_z() const { return gsl::ensure_z(sv_); } iterator begin() const { return sv_.begin(); } iterator end() const { return sv_.end(); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0415db3..774413f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ include_directories( ./unittest-cpp ) -add_definitions(-DSAFER_CPP_TESTING) +add_definitions(-DGSL_THROWS_FOR_TESTING) if(MSVC14 OR MSVC12) # has the support we need # remove unnecessary warnings about unchecked iterators diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index ba251e8..918df9e 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -27,7 +27,7 @@ using namespace std; -using namespace Guide; +using namespace gsl; namespace { @@ -261,8 +261,8 @@ SUITE(array_view_tests) int a[30][4][5]; auto av = as_array_view(a); - auto sub = av.section({15, 0, 0}, Guide::index<3>{2, 2, 2}); - auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1}); + 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}); } TEST(array_view_section) diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp index 012a043..acd381a 100644 --- a/tests/assertion_tests.cpp +++ b/tests/assertion_tests.cpp @@ -17,7 +17,7 @@ #include #include -using namespace Guide; +using namespace gsl; SUITE(assertion_tests) { diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp index 6a86307..d27dd9d 100644 --- a/tests/at_tests.cpp +++ b/tests/at_tests.cpp @@ -19,7 +19,7 @@ #include using namespace std; -using namespace Guide; +using namespace gsl; SUITE(at_tests) { diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index b14a113..c3f549f 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -19,7 +19,7 @@ #include using namespace std; -using namespace Guide;; +using namespace gsl;; namespace { diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp index 661b26a..74d449f 100644 --- a/tests/maybenull_tests.cpp +++ b/tests/maybenull_tests.cpp @@ -19,7 +19,7 @@ #include #include -using namespace Guide; +using namespace gsl; struct MyBase { bool foo() { return true; } }; struct MyDerived : public MyBase {}; diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index 46011b6..a9624b8 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -18,7 +18,7 @@ #include #include -using namespace Guide; +using namespace gsl; struct MyBase {}; struct MyDerived : public MyBase {}; diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index d985533..47c223a 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -18,7 +18,7 @@ #include #include -using namespace Guide; +using namespace gsl; SUITE(owner_tests) { diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp index c382cf0..84b7f3f 100644 --- a/tests/string_view_tests.cpp +++ b/tests/string_view_tests.cpp @@ -20,7 +20,7 @@ #include using namespace std; -using namespace Guide; +using namespace gsl; SUITE(string_view_tests) { diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 66b6d14..3090c7d 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -18,7 +18,7 @@ #include #include -using namespace Guide; +using namespace gsl; SUITE(utils_tests) { -- cgit v1.2.3 From 761554f68d8dfaa19f89b7b2a861035dbd9ff46d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 29 Sep 2015 16:54:00 -0700 Subject: Correct misleading text in static_assert. Fixes issue #67. --- include/array_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/array_view.h b/include/array_view.h index 9db9d53..c581760 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -77,7 +77,7 @@ namespace details class coordinate_facade { static_assert(std::is_integral::value - && sizeof(ValueType) <= sizeof(size_t), "ValueType must be unsigned integral type!"); + && sizeof(ValueType) <= sizeof(size_t), "ValueType must be an integral type!"); static_assert(Rank > 0, "Rank must be greater than 0!"); template -- cgit v1.2.3 From e8ff01e543fdb6a0be8f47d66c8279e7fcc72136 Mon Sep 17 00:00:00 2001 From: RicoAntonioFelix Date: Wed, 30 Sep 2015 12:50:42 -0400 Subject: Commit to address issue #103... --- include/fail_fast.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/fail_fast.h b/include/fail_fast.h index 477bace..b9982eb 100644 --- a/include/fail_fast.h +++ b/include/fail_fast.h @@ -20,7 +20,10 @@ #define GSL_FAIL_FAST_H #include + +#if defined(GSL_THROWS_FOR_TESTING) #include +#endif namespace gsl { -- cgit v1.2.3 From d06f7ff779824ca26b3c1daf0b31837334c9cf25 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 30 Sep 2015 12:39:18 -0700 Subject: Renamed Final_act to final_act as per issue #91. --- include/gsl.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 5ae4df6..5d4c840 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -45,30 +45,30 @@ using owner = T; // GSL.util: utilities // -// Final_act allows you to ensure something gets run at the end of a scope +// final_act allows you to ensure something gets run at the end of a scope template -class Final_act +class final_act { public: - explicit Final_act(F f) : f_(std::move(f)), invoke_(true) {} + explicit final_act(F f) : f_(std::move(f)), invoke_(true) {} - Final_act(Final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other.invoke_ = false; } - Final_act(const Final_act&) = delete; - Final_act& operator=(const Final_act&) = delete; + final_act(final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other.invoke_ = false; } + final_act(const final_act&) = delete; + final_act& operator=(const final_act&) = delete; - ~Final_act() { if (invoke_) f_(); } + ~final_act() { if (invoke_) f_(); } private: F f_; bool invoke_; }; -// finally() - convenience function to generate a Final_act +// finally() - convenience function to generate a final_act template -Final_act finally(const F &f) { return Final_act(f); } +final_act finally(const F &f) { return final_act(f); } template -Final_act finally(F &&f) { return Final_act(std::forward(f)); } +final_act finally(F &&f) { return final_act(std::forward(f)); } // narrow_cast(): a searchable way to do narrowing casts of values template -- cgit v1.2.3 From 831c6926df2dd17a691abf7d84137435ff172025 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 30 Sep 2015 15:10:24 -0700 Subject: Adding noexcept to finally, final_act, narrow_cast. Fixes #92. --- include/gsl.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 5d4c840..e15ca5c 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -50,13 +50,13 @@ template class final_act { public: - explicit final_act(F f) : f_(std::move(f)), invoke_(true) {} + explicit final_act(F f) noexcept : f_(std::move(f)), invoke_(true) {} - final_act(final_act&& other) : f_(std::move(other.f_)), invoke_(true) { other.invoke_ = false; } + final_act(final_act&& other) noexcept : f_(std::move(other.f_)), invoke_(true) { other.invoke_ = false; } final_act(const final_act&) = delete; final_act& operator=(const final_act&) = delete; - ~final_act() { if (invoke_) f_(); } + ~final_act() noexcept { if (invoke_) f_(); } private: F f_; @@ -65,14 +65,14 @@ private: // finally() - convenience function to generate a final_act template -final_act finally(const F &f) { return final_act(f); } +final_act finally(const F &f) noexcept { return final_act(f); } template -final_act finally(F &&f) { return final_act(std::forward(f)); } +final_act finally(F &&f) noexcept { return final_act(std::forward(f)); } // narrow_cast(): a searchable way to do narrowing casts of values template -T narrow_cast(U u) { return static_cast(u); } +T narrow_cast(U u) noexcept { return static_cast(u); } struct narrowing_error : public std::exception {}; // narrow() : a checked version of narrow_cast() that throws if the cast changed the value -- cgit v1.2.3 From d531680a31919bf2220585d22f13f90203606004 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 30 Sep 2015 21:54:08 -0700 Subject: Improved macros used for non-compliant compilers. --- include/array_view.h | 414 ++++++++++++++++++++++++++------------------------- include/gsl.h | 48 ++++++ 2 files changed, 260 insertions(+), 202 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index c581760..3fc675e 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -30,35 +30,35 @@ #include #include "fail_fast.h" -#if defined(_MSC_VER) +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet #pragma push_macro("constexpr") #define constexpr /* nothing */ -#endif - -#pragma push_macro("_NOEXCEPT") - -#ifndef _NOEXCEPT - -#ifdef GSL_THROWS_FOR_TESTING -#define _NOEXCEPT -#else -#define _NOEXCEPT noexcept -#endif -#else // _NOEXCEPT -#ifdef GSL_THROWS_FOR_TESTING -#undef _NOEXCEPT -#define _NOEXCEPT -#endif +// VS 2013 workarounds +#if _MSC_VER <= 1800 -#endif // _NOEXCEPT +// noexcept is not understood +#ifndef GSL_THROWS_FOR_TESTING +#define noexcept /* nothing */ +#endif -#if defined(_MSC_VER) && _MSC_VER <= 1800 +// 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 + +// 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 { /* @@ -87,17 +87,17 @@ namespace details using const_reference = const ValueType&; using value_type = ValueType; static const size_t rank = Rank; - constexpr coordinate_facade() _NOEXCEPT + constexpr coordinate_facade() noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); } - constexpr coordinate_facade(const value_type(&values)[rank]) _NOEXCEPT + constexpr coordinate_facade(const value_type(&values)[rank]) noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); for (size_t i = 0; i < rank; ++i) elems[i] = values[i]; } - constexpr coordinate_facade(value_type e0) _NOEXCEPT + constexpr coordinate_facade(value_type e0) noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); static_assert(rank == 1, "This constructor can only be used with rank == 1."); @@ -139,7 +139,7 @@ namespace details fail_fast_assert(component_idx < rank, "Component index must be less than rank"); return elems[component_idx]; } - constexpr bool operator==(const ConcreteType& rhs) const _NOEXCEPT + constexpr bool operator==(const ConcreteType& rhs) const noexcept { for (size_t i = 0; i < rank; ++i) { @@ -148,11 +148,11 @@ namespace details } return true; } - constexpr bool operator!=(const ConcreteType& rhs) const _NOEXCEPT + constexpr bool operator!=(const ConcreteType& rhs) const noexcept { return !(to_concrete() == rhs); } - constexpr ConcreteType operator+() const _NOEXCEPT + constexpr ConcreteType operator+() const noexcept { return to_concrete(); } @@ -243,11 +243,11 @@ namespace details } value_type elems[rank] = {}; private: - constexpr const ConcreteType& to_concrete() const _NOEXCEPT + constexpr const ConcreteType& to_concrete() const noexcept { return static_cast(*this); } - constexpr ConcreteType& to_concrete() _NOEXCEPT + constexpr ConcreteType& to_concrete() noexcept { return static_cast(*this); } @@ -259,11 +259,11 @@ namespace details explicit arrow_proxy(T t) : val(t) {} - const T operator*() const _NOEXCEPT + const T operator*() const noexcept { return val; } - const T* operator->() const _NOEXCEPT + const T* operator->() const noexcept { return &val; } @@ -285,8 +285,8 @@ public: using const_reference = typename Base::const_reference; using size_type = typename Base::value_type; using value_type = typename Base::value_type; - constexpr index() _NOEXCEPT : Base(){} - constexpr index(const value_type (&values)[rank]) _NOEXCEPT : Base(values) {} + constexpr index() noexcept : Base(){} + constexpr index(const value_type (&values)[rank]) noexcept : Base(values) {} constexpr index(std::initializer_list il) : Base(il) {} constexpr index(const index &) = default; @@ -295,7 +295,7 @@ public: constexpr index(const index &other) : Base(other) { } - constexpr static index shift_left(const index& other) _NOEXCEPT + constexpr static index shift_left(const index& other) noexcept { value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); return index(arr); @@ -328,13 +328,13 @@ public: using size_type = ValueType; using value_type = ValueType; - constexpr index() _NOEXCEPT : value(0) + constexpr index() noexcept : value(0) { } - constexpr index(value_type e0) _NOEXCEPT : value(e0) + constexpr index(value_type e0) noexcept : value(e0) { } - constexpr index(const value_type(&values)[1]) _NOEXCEPT : index(values[0]) + constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) { } // Preconditions: il.size() == rank @@ -353,99 +353,99 @@ public: value = static_cast(other.value); } - constexpr static index shift_left(const index& other) _NOEXCEPT + constexpr static index shift_left(const index& other) noexcept { return other.elems[1]; } // Preconditions: component_idx < rank - constexpr reference operator[](size_type component_idx) _NOEXCEPT + constexpr reference operator[](size_type component_idx) noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } // Preconditions: component_idx < rank - constexpr const_reference operator[](size_type component_idx) const _NOEXCEPT + constexpr const_reference operator[](size_type component_idx) const noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } - constexpr bool operator==(const index& rhs) const _NOEXCEPT + constexpr bool operator==(const index& rhs) const noexcept { return value == rhs.value; } - constexpr bool operator!=(const index& rhs) const _NOEXCEPT + constexpr bool operator!=(const index& rhs) const noexcept { return !(*this == rhs); } - constexpr index operator+() const _NOEXCEPT + constexpr index operator+() const noexcept { return *this; } - constexpr index operator-() const _NOEXCEPT + constexpr index operator-() const noexcept { return index(-value); } - constexpr index operator+(const index& rhs) const _NOEXCEPT + constexpr index operator+(const index& rhs) const noexcept { return index(value + rhs.value); } - constexpr index operator-(const index& rhs) const _NOEXCEPT + constexpr index operator-(const index& rhs) const noexcept { return index(value - rhs.value); } - constexpr index& operator+=(const index& rhs) _NOEXCEPT + constexpr index& operator+=(const index& rhs) noexcept { value += rhs.value; return *this; } - constexpr index& operator-=(const index& rhs) _NOEXCEPT + constexpr index& operator-=(const index& rhs) noexcept { value -= rhs.value; return *this; } - constexpr index& operator++() _NOEXCEPT + constexpr index& operator++() noexcept { ++value; return *this; } - constexpr index operator++(int) _NOEXCEPT + constexpr index operator++(int) noexcept { index ret = *this; ++(*this); return ret; } - constexpr index& operator--() _NOEXCEPT + constexpr index& operator--() noexcept { --value; return *this; } - constexpr index operator--(int) _NOEXCEPT + constexpr index operator--(int) noexcept { index ret = *this; --(*this); return ret; } - constexpr index operator*(value_type v) const _NOEXCEPT + constexpr index operator*(value_type v) const noexcept { return index(value * v); } - constexpr index operator/(value_type v) const _NOEXCEPT + constexpr index operator/(value_type v) const noexcept { return index(value / v); } - constexpr index& operator*=(value_type v) _NOEXCEPT + constexpr index& operator*=(value_type v) noexcept { value *= v; return *this; } - constexpr index& operator/=(value_type v) _NOEXCEPT + constexpr index& operator/=(value_type v) noexcept { value /= v; return *this; } - friend constexpr index operator*(value_type v, const index& rhs) _NOEXCEPT + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return index(rhs * v); } @@ -565,11 +565,11 @@ namespace details return 0; } - size_t totalSize() const _NOEXCEPT { + size_t totalSize() const noexcept { return TotalSize; } - bool operator == (const BoundsRanges &) const _NOEXCEPT + bool operator == (const BoundsRanges &) const noexcept { return true; } @@ -619,22 +619,22 @@ namespace details return static_cast(cur) < static_cast(m_bound) ? cur + last : -1; } - size_t totalSize() const _NOEXCEPT { + size_t totalSize() const noexcept { return m_bound; } - SizeType elementNum() const _NOEXCEPT { + SizeType elementNum() const noexcept { return static_cast(totalSize() / this->Base::totalSize()); } - SizeType elementNum(size_t dim) const _NOEXCEPT{ + SizeType elementNum(size_t dim) const noexcept{ if (dim > 0) return this->Base::elementNum(dim - 1); else return elementNum(); } - bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + bool operator == (const BoundsRanges & rhs) const noexcept { return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); } @@ -681,22 +681,22 @@ namespace details return static_cast(this->Base::totalSize() * arr[Dim]) + last; } - size_t totalSize() const _NOEXCEPT{ + size_t totalSize() const noexcept{ return CurrentRange * this->Base::totalSize(); } - SizeType elementNum() const _NOEXCEPT{ + SizeType elementNum() const noexcept{ return CurrentRange; } - SizeType elementNum(size_t dim) const _NOEXCEPT{ + SizeType elementNum(size_t dim) const noexcept{ if (dim > 0) return this->Base::elementNum(dim - 1); else return elementNum(); } - bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + bool operator == (const BoundsRanges & rhs) const noexcept { return static_cast(*this) == static_cast(rhs); } @@ -816,22 +816,22 @@ public: return *this; } - constexpr sliced_type slice() const _NOEXCEPT + constexpr sliced_type slice() const noexcept { return sliced_type{static_cast &>(m_ranges)}; } - constexpr size_type stride() const _NOEXCEPT + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } - constexpr size_type size() const _NOEXCEPT + constexpr size_type size() const noexcept { return static_cast(m_ranges.totalSize()); } - constexpr size_type total_size() const _NOEXCEPT + constexpr size_type total_size() const noexcept { return static_cast(m_ranges.totalSize()); } @@ -841,24 +841,24 @@ public: return m_ranges.linearize(idx); } - constexpr bool contains(const index_type& idx) const _NOEXCEPT + constexpr bool contains(const index_type& idx) const noexcept { return m_ranges.contains(idx) != -1; } - constexpr size_type operator[](size_t index) const _NOEXCEPT + constexpr size_type operator[](size_t index) const noexcept { return m_ranges.elementNum(index); } template - constexpr size_type extent() const _NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return details::createTypeListIndexer(m_ranges).template get().elementNum(); } - constexpr index_type index_bounds() const _NOEXCEPT + constexpr index_type index_bounds() const noexcept { index_type extents; m_ranges.serialize(extents); @@ -866,23 +866,23 @@ public: } template - constexpr bool operator == (const static_bounds & rhs) const _NOEXCEPT + constexpr bool operator == (const static_bounds & rhs) const noexcept { return this->size() == rhs.size(); } template - constexpr bool operator != (const static_bounds & rhs) const _NOEXCEPT + constexpr bool operator != (const static_bounds & rhs) const noexcept { return !(*this == rhs); } - constexpr const_iterator begin() const _NOEXCEPT + constexpr const_iterator begin() const noexcept { return const_iterator(*this); } - constexpr const_iterator end() const _NOEXCEPT + constexpr const_iterator end() const noexcept { index_type boundary; m_ranges.serialize(boundary); @@ -930,25 +930,25 @@ public: : Base(values), m_strides(std::move(strides)) { } - constexpr index_type strides() const _NOEXCEPT + constexpr index_type strides() const noexcept { return m_strides; } - constexpr size_type total_size() const _NOEXCEPT + constexpr size_type total_size() const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; ++i) ret += (Base::elems[i] - 1) * m_strides[i]; return ret + 1; } - constexpr size_type size() const _NOEXCEPT + constexpr size_type size() const noexcept { size_type ret = 1; for (size_t i = 0; i < rank; ++i) ret *= Base::elems[i]; return ret; } - constexpr bool contains(const index_type& idx) const _NOEXCEPT + constexpr bool contains(const index_type& idx) const noexcept { for (size_t i = 0; i < rank; ++i) { @@ -967,7 +967,7 @@ public: } return ret; } - constexpr size_type stride() const _NOEXCEPT + constexpr size_type stride() const noexcept { return m_strides[0]; } @@ -977,20 +977,20 @@ public: return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; } template - constexpr size_type extent() const _NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); return Base::elems[Dim]; } - constexpr index_type index_bounds() const _NOEXCEPT + constexpr index_type index_bounds() const noexcept { return index_type(Base::elems); } - const_iterator begin() const _NOEXCEPT + const_iterator begin() const noexcept { return const_iterator{ *this }; } - const_iterator end() const _NOEXCEPT + const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } @@ -1024,21 +1024,21 @@ public: using index_type = value_type; using index_size_type = typename IndexType::size_type; template - explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) _NOEXCEPT + explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) noexcept : boundary(bnd.index_bounds()) , curr( std::move(curr) ) { static_assert(is_bounds::value, "Bounds type must be provided"); } - reference operator*() const _NOEXCEPT + reference operator*() const noexcept { return curr; } - pointer operator->() const _NOEXCEPT + pointer operator->() const noexcept { return details::arrow_proxy{ curr }; } - bounds_iterator& operator++() _NOEXCEPT + bounds_iterator& operator++() noexcept { for (size_t i = rank; i-- > 0;) { @@ -1058,13 +1058,13 @@ public: } return *this; } - bounds_iterator operator++(int) _NOEXCEPT + bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() _NOEXCEPT + bounds_iterator& operator--() noexcept { for (size_t i = rank; i-- > 0;) { @@ -1082,18 +1082,18 @@ public: fail_fast_assert(false); return *this; } - bounds_iterator operator--(int) _NOEXCEPT + bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const _NOEXCEPT + bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) _NOEXCEPT + bounds_iterator& operator+=(difference_type n) noexcept { auto linear_idx = linearize(curr) + n; value_type stride; @@ -1109,32 +1109,32 @@ public: } return *this; } - bounds_iterator operator-(difference_type n) const _NOEXCEPT + bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) _NOEXCEPT + bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr) - linearize(rhs.curr); } - reference operator[](difference_type n) const _NOEXCEPT + reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + bool operator<(const bounds_iterator& rhs) const noexcept { for (size_t i = 0; i < rank; ++i) { @@ -1143,25 +1143,25 @@ public: } return false; } - bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(bounds_iterator& rhs) _NOEXCEPT + void swap(bounds_iterator& rhs) noexcept { std::swap(boundary, rhs.boundary); std::swap(curr, rhs.curr); } private: - index_size_type linearize(const value_type& idx) const _NOEXCEPT + index_size_type linearize(const value_type& idx) const noexcept { // TODO: Smarter impl. // Check if past-the-end @@ -1218,91 +1218,91 @@ public: using index_size_type = typename index_type::size_type; template - explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) _NOEXCEPT + explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) noexcept : curr( std::move(curr) ) {} - reference operator*() const _NOEXCEPT + reference operator*() const noexcept { return curr; } - pointer operator->() const _NOEXCEPT + pointer operator->() const noexcept { return details::arrow_proxy{ curr }; } - bounds_iterator& operator++() _NOEXCEPT + bounds_iterator& operator++() noexcept { ++curr; return *this; } - bounds_iterator operator++(int) _NOEXCEPT + bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() _NOEXCEPT + bounds_iterator& operator--() noexcept { curr--; return *this; } - bounds_iterator operator--(int) _NOEXCEPT + bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const _NOEXCEPT + bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) _NOEXCEPT + bounds_iterator& operator+=(difference_type n) noexcept { curr += n; return *this; } - bounds_iterator operator-(difference_type n) const _NOEXCEPT + bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) _NOEXCEPT + bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + difference_type operator-(const bounds_iterator& rhs) const noexcept { return curr[0] - rhs.curr[0]; } - reference operator[](difference_type n) const _NOEXCEPT + reference operator[](difference_type n) const noexcept { return curr + n; } - bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + bool operator<(const bounds_iterator& rhs) const noexcept { return curr[0] < rhs.curr[0]; } - bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(bounds_iterator& rhs) _NOEXCEPT + void swap(bounds_iterator& rhs) noexcept { std::swap(curr, rhs.curr); } @@ -1311,7 +1311,7 @@ private: }; template -bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) _NOEXCEPT +bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept { return rhs + n; } @@ -1322,14 +1322,14 @@ bounds_iterator operator+(typename bounds_iterator::differ namespace details { template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { return bnd.strides(); } // Make a stride vector from bounds, assuming contiguous memory. template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); typename Bounds::index_type stride; @@ -1379,17 +1379,17 @@ private: bounds_type m_bounds; public: - constexpr bounds_type bounds() const _NOEXCEPT + constexpr bounds_type bounds() const noexcept { return m_bounds; } template - constexpr size_type extent() const _NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); return m_bounds.template extent(); } - constexpr size_type size() const _NOEXCEPT + constexpr size_type size() const noexcept { return m_bounds.size(); } @@ -1397,7 +1397,7 @@ public: { return m_pdata[m_bounds.linearize(idx)]; } - constexpr pointer data() const _NOEXCEPT + constexpr pointer data() const noexcept { return m_pdata; } @@ -1411,7 +1411,7 @@ public: return Ret {m_pdata + ridx, m_bounds.slice()}; } - constexpr operator bool () const _NOEXCEPT + constexpr operator bool () const noexcept { return m_pdata != nullptr; } @@ -1451,38 +1451,38 @@ public: } template , std::remove_cv_t>::value>> - constexpr bool operator== (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator== (const basic_array_view & other) const noexcept { return m_bounds.size() == other.m_bounds.size() && (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); } template , std::remove_cv_t>::value>> - constexpr bool operator!= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator!= (const basic_array_view & other) const noexcept { return !(*this == other); } template , std::remove_cv_t>::value>> - constexpr bool operator< (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator< (const basic_array_view & other) const noexcept { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> - constexpr bool operator<= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator<= (const basic_array_view & other) const noexcept { return !(other < *this); } template , std::remove_cv_t>::value>> - constexpr bool operator> (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator> (const basic_array_view & other) const noexcept { return (other < *this); } template , std::remove_cv_t>::value>> - constexpr bool operator>= (const basic_array_view & other) const _NOEXCEPT + constexpr bool operator>= (const basic_array_view & other) const noexcept { return !(*this < other); } @@ -1491,20 +1491,20 @@ public: template ::value && std::is_convertible::value>> - constexpr basic_array_view(const basic_array_view & other ) _NOEXCEPT + constexpr basic_array_view(const basic_array_view & other ) noexcept : m_pdata(other.m_pdata), m_bounds(other.m_bounds) { } protected: - constexpr basic_array_view(pointer data, bounds_type bound) _NOEXCEPT + constexpr basic_array_view(pointer data, bounds_type bound) noexcept : m_pdata(data) , m_bounds(std::move(bound)) { fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } template - constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) _NOEXCEPT + constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept : m_pdata(reinterpret_cast(data)) , m_bounds(std::move(bound)) { @@ -1759,7 +1759,7 @@ public: // to bytes array template ::value_type>>::value> - constexpr auto as_bytes() const _NOEXCEPT -> + constexpr auto as_bytes() const noexcept -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); @@ -1767,7 +1767,7 @@ public: } template ::value_type>>::value> - constexpr auto as_writeable_bytes() const _NOEXCEPT -> + constexpr auto as_writeable_bytes() const noexcept -> array_view, static_cast(details::StaticSizeHelper::value)> { static_assert(Enabled, "The value_type of array_view must be standarded layout"); @@ -1777,7 +1777,7 @@ public: // from bytes array template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + constexpr auto as_array_view() const noexcept -> array_view(dynamic_range))> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1786,7 +1786,7 @@ public: } template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + constexpr auto as_array_view() const noexcept -> array_view(dynamic_range))> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1796,61 +1796,61 @@ public: // section on linear space template - constexpr array_view first() const _NOEXCEPT + constexpr array_view first() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed return { this->data(), Count }; } - constexpr array_view first(size_type count) const _NOEXCEPT + constexpr array_view first(size_type count) const noexcept { fail_fast_assert(count <= this->size()); return { this->data(), count }; } template - constexpr array_view last() const _NOEXCEPT + constexpr array_view last() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data() + this->size() - Count, Count }; } - constexpr array_view last(size_type count) const _NOEXCEPT + constexpr array_view last(size_type count) const noexcept { fail_fast_assert(count <= this->size()); return { this->data() + this->size() - count, count }; } template - constexpr array_view sub() const _NOEXCEPT + constexpr array_view sub() const noexcept { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } - constexpr array_view sub(size_type offset, size_type count = dynamic_range) const _NOEXCEPT + constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept { fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; } // size - constexpr size_type length() const _NOEXCEPT + constexpr size_type length() const noexcept { return this->size(); } - constexpr size_type used_length() const _NOEXCEPT + constexpr size_type used_length() const noexcept { return length(); } - constexpr size_type bytes() const _NOEXCEPT + constexpr size_type bytes() const noexcept { return sizeof(value_type) * this->size(); } - constexpr size_type used_bytes() const _NOEXCEPT + constexpr size_type used_bytes() const noexcept { return bytes(); } @@ -2064,93 +2064,93 @@ private: contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } public: - reference operator*() const _NOEXCEPT + reference operator*() const noexcept { validateThis(); return *m_pdata; } - pointer operator->() const _NOEXCEPT + pointer operator->() const noexcept { validateThis(); return m_pdata; } - contiguous_array_view_iterator& operator++() _NOEXCEPT + contiguous_array_view_iterator& operator++() noexcept { ++m_pdata; return *this; } - contiguous_array_view_iterator operator++(int)_NOEXCEPT + contiguous_array_view_iterator operator++(int)noexcept { auto ret = *this; ++(*this); return ret; } - contiguous_array_view_iterator& operator--() _NOEXCEPT + contiguous_array_view_iterator& operator--() noexcept { --m_pdata; return *this; } - contiguous_array_view_iterator operator--(int)_NOEXCEPT + contiguous_array_view_iterator operator--(int)noexcept { auto ret = *this; --(*this); return ret; } - contiguous_array_view_iterator operator+(difference_type n) const _NOEXCEPT + contiguous_array_view_iterator operator+(difference_type n) const noexcept { contiguous_array_view_iterator ret{ *this }; return ret += n; } - contiguous_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + contiguous_array_view_iterator& operator+=(difference_type n) noexcept { m_pdata += n; return *this; } - contiguous_array_view_iterator operator-(difference_type n) const _NOEXCEPT + contiguous_array_view_iterator operator-(difference_type n) const noexcept { contiguous_array_view_iterator ret{ *this }; return ret -= n; } - contiguous_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + contiguous_array_view_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + difference_type operator-(const contiguous_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_validator == rhs.m_validator); return m_pdata - rhs.m_pdata; } - reference operator[](difference_type n) const _NOEXCEPT + reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator==(const contiguous_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_validator == rhs.m_validator); return m_pdata == rhs.m_pdata; } - bool operator!=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator!=(const contiguous_array_view_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator<(const contiguous_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_validator == rhs.m_validator); return m_pdata < rhs.m_pdata; } - bool operator<=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator<=(const contiguous_array_view_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator>(const contiguous_array_view_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + bool operator>=(const contiguous_array_view_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(contiguous_array_view_iterator& rhs) _NOEXCEPT + void swap(contiguous_array_view_iterator& rhs) noexcept { std::swap(m_pdata, rhs.m_pdata); std::swap(m_validator, rhs.m_validator); @@ -2158,7 +2158,7 @@ public: }; template -contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) _NOEXCEPT +contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) noexcept { return rhs + n; } @@ -2182,91 +2182,91 @@ private: { } public: - reference operator*() const _NOEXCEPT + reference operator*() const noexcept { return (*m_container)[*m_itr]; } - pointer operator->() const _NOEXCEPT + pointer operator->() const noexcept { return &(*m_container)[*m_itr]; } - general_array_view_iterator& operator++() _NOEXCEPT + general_array_view_iterator& operator++() noexcept { ++m_itr; return *this; } - general_array_view_iterator operator++(int)_NOEXCEPT + general_array_view_iterator operator++(int)noexcept { auto ret = *this; ++(*this); return ret; } - general_array_view_iterator& operator--() _NOEXCEPT + general_array_view_iterator& operator--() noexcept { --m_itr; return *this; } - general_array_view_iterator operator--(int)_NOEXCEPT + general_array_view_iterator operator--(int)noexcept { auto ret = *this; --(*this); return ret; } - general_array_view_iterator operator+(difference_type n) const _NOEXCEPT + general_array_view_iterator operator+(difference_type n) const noexcept { general_array_view_iterator ret{ *this }; return ret += n; } - general_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + general_array_view_iterator& operator+=(difference_type n) noexcept { m_itr += n; return *this; } - general_array_view_iterator operator-(difference_type n) const _NOEXCEPT + general_array_view_iterator operator-(difference_type n) const noexcept { general_array_view_iterator ret{ *this }; return ret -= n; } - general_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + general_array_view_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const general_array_view_iterator& rhs) const _NOEXCEPT + difference_type operator-(const general_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_container == rhs.m_container); return m_itr - rhs.m_itr; } - value_type operator[](difference_type n) const _NOEXCEPT + value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]];; } - bool operator==(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator==(const general_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_container == rhs.m_container); return m_itr == rhs.m_itr; } - bool operator !=(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator !=(const general_array_view_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator<(const general_array_view_iterator& rhs) const noexcept { fail_fast_assert(m_container == rhs.m_container); return m_itr < rhs.m_itr; } - bool operator<=(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator<=(const general_array_view_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator>(const general_array_view_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const general_array_view_iterator& rhs) const _NOEXCEPT + bool operator>=(const general_array_view_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(general_array_view_iterator& rhs) _NOEXCEPT + void swap(general_array_view_iterator& rhs) noexcept { std::swap(m_itr, rhs.m_itr); std::swap(m_container, rhs.m_container); @@ -2274,22 +2274,32 @@ public: }; template -general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) _NOEXCEPT +general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) noexcept { return rhs + n; } } // namespace gsl -#if defined(_MSC_VER) +#ifdef _MSC_VER + #undef constexpr #pragma pop_macro("constexpr") -#endif -#if defined(_MSC_VER) && _MSC_VER <= 1800 +#if _MSC_VER <= 1800 #pragma warning(pop) + +#ifndef GSL_THROWS_FOR_TESTING +#pragma undef noexcept +#endif // GSL_THROWS_FOR_TESTING + #endif // _MSC_VER <= 1800 -#pragma pop_macro("_NOEXCEPT") +#endif // _MSC_VER + +#if defined(GSL_THROWS_FOR_TESTING) +#undef noexcept +#endif // GSL_THROWS_FOR_TESTING + #endif // GSL_ARRAY_VIEW_H diff --git a/include/gsl.h b/include/gsl.h index e15ca5c..dd3d971 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -23,6 +23,34 @@ #include "string_view.h" // zstring, string_view, zstring_builder... #include +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 + +// noexcept is not understood +#ifndef GSL_THROWS_FOR_TESTING +#define noexcept /* nothing */ +#endif + +// 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 + +// 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 { @@ -356,4 +384,24 @@ template using maybe_null = maybe_null_ret; } // namespace gsl +#ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + +#if _MSC_VER <= 1800 +#pragma warning(pop) + +#ifndef GSL_THROWS_FOR_TESTING +#pragma undef noexcept +#endif // GSL_THROWS_FOR_TESTING + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROWS_FOR_TESTING) +#undef noexcept +#endif // GSL_THROWS_FOR_TESTING + #endif // GSL_GSL_H -- cgit v1.2.3 From dd923eee5f02dd7bae935dfa95da570e29c50c51 Mon Sep 17 00:00:00 2001 From: Seth Cantrell Date: Thu, 1 Oct 2015 20:53:37 -0400 Subject: Add column limit to .clang-format --- .clang-format | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.clang-format b/.clang-format index bef7ebc..b80d2c6 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,4 @@ -ColumnLimit: 0 +ColumnLimit: 100 UseTab: Never IndentWidth: 4 @@ -6,7 +6,7 @@ AccessModifierOffset: -4 NamespaceIndentation: Inner BreakBeforeBraces: Allman -AlwaysBreakTemplateDeclarations: false +AlwaysBreakTemplateDeclarations: true BreakConstructorInitializersBeforeComma: true ConstructorInitializerAllOnOneLineOrOnePerLine: true AllowShortBlocksOnASingleLine: true -- cgit v1.2.3 From b8ec73a1790fb682037d6f5ca7109e7d5dfddb06 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Fri, 2 Oct 2015 15:58:23 -0700 Subject: Fix moving a final_act twice --- include/gsl.h | 2 +- tests/utils_tests.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index dd3d971..824ca6a 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -80,7 +80,7 @@ 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_(true) { other.invoke_ = false; } + 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; diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 3090c7d..9406e6b 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -44,9 +44,14 @@ SUITE(utils_tests) auto _1 = finally([&]() {f(i);}); { auto _2 = std::move(_1); - CHECK(i == 0); + CHECK(i == 0); } - CHECK(i == 1); + CHECK(i == 1); + { + auto _2 = std::move(_1); + CHECK(i == 1); + } + CHECK(i == 1); } CHECK(i == 1); } -- cgit v1.2.3 From 144ee44132d920ac8d8189c32ad07853119cd2e4 Mon Sep 17 00:00:00 2001 From: kkoenig Date: Sat, 3 Oct 2015 22:02:49 -0700 Subject: First arg of vector ctor should be num elements, second should be value. Fixes issue #121 --- tests/string_view_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp index 84b7f3f..fb57845 100644 --- a/tests/string_view_tests.cpp +++ b/tests/string_view_tests.cpp @@ -45,7 +45,7 @@ SUITE(string_view_tests) TEST(TestConstructFromStdVector) { - std::vector vec('h', 5); + std::vector vec(5, 'h'); string_view<> v = vec; CHECK(v.length() == vec.size()); } -- cgit v1.2.3 From fda8e1231008bdee8f5356a241ae9fd4ffc19872 Mon Sep 17 00:00:00 2001 From: Treb Connell Date: Mon, 5 Oct 2015 13:34:50 -0700 Subject: Fix #124 remove maybe_null --- include/gsl.h | 187 ---------------------------- tests/CMakeLists.txt | 1 - tests/maybenull_tests.cpp | 304 ---------------------------------------------- 3 files changed, 492 deletions(-) delete mode 100644 tests/maybenull_tests.cpp diff --git a/include/gsl.h b/include/gsl.h index 824ca6a..519682b 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -195,193 +195,6 @@ private: not_null& operator-=(size_t) = delete; }; - -// -// maybe_null -// -// Describes an optional pointer - provides symmetry with not_null -// -template -class maybe_null_ret; - -template -class maybe_null_dbg -{ - template - friend class maybe_null_dbg; - - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); -public: - maybe_null_dbg() : ptr_(nullptr), tested_(false) {} - maybe_null_dbg(std::nullptr_t) : ptr_(nullptr), tested_(false) {} - - maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} - maybe_null_dbg& operator=(const T& p) - { - if (ptr_ != p) - { - ptr_ = p; - tested_ = false; - } - return *this; - } - - - maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} - maybe_null_dbg& operator=(const maybe_null_dbg& rhs) - { - if (this != &rhs) - { - ptr_ = rhs.ptr_; - tested_ = false; - } - return *this; - } - - - template ::value>> - maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const not_null &other) - { - ptr_ = other.get(); - tested_ = false; - return *this; - } - - - template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.ptr_), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const maybe_null_dbg &other) - { - ptr_ = other.ptr_; - tested_ = false; - return *this; - } - - - template ::value>> - maybe_null_dbg(const maybe_null_ret &other) : ptr_(other.get()), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const maybe_null_ret &other) - { - ptr_ = other.get(); - tested_ = false; - return *this; - } - - - bool present() const { tested_ = true; return ptr_ != nullptr; } - - bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } - bool operator!=(const T& rhs) const { return !(*this == rhs); } - template ::value>> - bool operator==(const maybe_null_dbg& rhs) const { tested_ = true; rhs.tested_ = true; return ptr_ == rhs.ptr_; } - template ::value>> - bool operator!=(const maybe_null_dbg& rhs) const { return !(*this == rhs); } - - T get() const { - fail_fast_assert(tested_); -#ifdef _MSC_VER - __assume(ptr_ != nullptr); -#endif - return ptr_; - } - - operator T() const { return get(); } - T operator->() const { return get(); } - -private: - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - maybe_null_dbg& operator++() = delete; - maybe_null_dbg& operator--() = delete; - maybe_null_dbg operator++(int) = delete; - maybe_null_dbg operator--(int) = delete; - maybe_null_dbg& operator+(size_t) = delete; - maybe_null_dbg& operator+=(size_t) = delete; - maybe_null_dbg& operator-(size_t) = delete; - maybe_null_dbg& operator-=(size_t) = delete; - - T ptr_; - mutable bool tested_; -}; - -template -class maybe_null_ret -{ - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); -public: - maybe_null_ret() : ptr_(nullptr) {} - maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} - - maybe_null_ret(const T& p) : ptr_(p) {} - maybe_null_ret& operator=(const T& p) { ptr_ = p; return *this; } - - maybe_null_ret(const maybe_null_ret& rhs) = default; - maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; - - template ::value>> - maybe_null_ret(const not_null &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const not_null &other) - { - ptr_ = other.get(); - return *this; - } - - - template ::value>> - maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const maybe_null_ret &other) - { - ptr_ = other.get(); - return *this; - } - - - template ::value>> - maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const maybe_null_dbg &other) - { - ptr_ = other.get(); - return *this; - } - - - bool present() const { return ptr_ != nullptr; } - - T get() const { return ptr_; } - - operator T() const { return get(); } - T operator->() const { return get(); } - -private: - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - maybe_null_ret& operator++() = delete; - maybe_null_ret& operator--() = delete; - maybe_null_ret operator++(int) = delete; - maybe_null_ret operator--(int) = delete; - maybe_null_ret& operator+(size_t) = delete; - maybe_null_ret& operator+=(size_t) = delete; - maybe_null_ret& operator-(size_t) = delete; - maybe_null_ret& operator-=(size_t) = delete; - - T ptr_; -}; - -template using maybe_null = maybe_null_ret; - } // namespace gsl #ifdef _MSC_VER diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 774413f..5e4c395 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -47,7 +47,6 @@ add_gsl_test(array_view_tests) add_gsl_test(string_view_tests) add_gsl_test(at_tests) add_gsl_test(bounds_tests) -add_gsl_test(maybenull_tests) add_gsl_test(notnull_tests) add_gsl_test(assertion_tests) add_gsl_test(utils_tests) diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp deleted file mode 100644 index 74d449f..0000000 --- a/tests/maybenull_tests.cpp +++ /dev/null @@ -1,304 +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 -#include -#include -#include - -using namespace gsl; - -struct MyBase { bool foo() { return true; } }; -struct MyDerived : public MyBase {}; -struct Unrelated {}; - -SUITE(MaybeNullTests) -{ - TEST(TestMaybeNull1) - { -#ifdef CONFIRM_COMPILATION_ERRORS - // Forbid non-nullptr assignable types - maybe_null_ret> f_ret(std::vector{1}); - maybe_null_ret> f_ret(std::vector{1}); - maybe_null_ret z_ret(10); - maybe_null_dbg> y_dbg({1,2}); - maybe_null_dbg z_dbg(10); - maybe_null_dbg> y_dbg({1,2}); -#endif - int n = 5; - maybe_null_dbg opt_n(&n); - int result = 0; - bool threw = false; - - CHECK_THROW(result = *opt_n, fail_fast); - - maybe_null_ret> x_ret(std::make_shared(10)); // shared_ptr is nullptr assignable - maybe_null_dbg> x_dbg(std::make_shared(10)); // shared_ptr is nullptr assignable - } - - TEST(TestMaybeNull2) - { - int n = 5; - maybe_null opt_n(&n); - int result = 0; - if (opt_n.present()) - result = *opt_n; - } - - TEST(TestMaybeNull3) - { - int n = 5; - maybe_null opt_n(&n); - int result = 0; - if (opt_n != nullptr) - result = *opt_n; - } - - int test4_helper(maybe_null p) - { - if (p != nullptr) - return *p; - return -1; - } - - TEST(TestMaybeNull4) - { - int n = 5; - int result = 0; - result = test4_helper(&n); - } - - int test5_helper(maybe_null_dbg p) - { - return *p; - } - - TEST(TestMaybeNull5) - { - int n = 5; - int result = 0; - bool threw = false; - - CHECK_THROW(result = test5_helper(&n), fail_fast); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - int TestMaybeNull6() - { - int n; - maybe_null o(n); - } -#endif - - int g_int; - void test7_helper(maybe_null *> outptr) - { - g_int = 5; - - if (outptr.present()) - *outptr = &g_int; - } - - void test7b_helper(maybe_null_dbg *> outptr) - { - g_int = 5; - - if (outptr.present()) - *outptr = &g_int; - } - - TEST(TestMaybeNull7a) - { - maybe_null outval; - test7_helper(&outval); - CHECK(outval.present() && *outval == 5); - } - - TEST(TestMaybeNull7b) - { - maybe_null_dbg outval; - test7b_helper(&outval); - CHECK_THROW((void)*outval, fail_fast); - } - - int test8_helper1(maybe_null_dbg opt) - { - return *opt; - } - - int test8_helper2a(maybe_null_dbg opt) - { - if (!opt.present()) - return 0; - return test8_helper1(opt); - } - - TEST(TestMaybeNull8a) - { - int n = 5; - maybe_null_dbg opt(&n); - CHECK_THROW(test8_helper2a(opt), fail_fast); - } - -#ifdef CONVERT_TO_PTR_TO_CONST - int test9_helper(maybe_null copt) - { - if (copt.present()) - return *copt; - return 0; - } - - void TestMaybeNull9() - { - int n = 5; - maybe_null opt(&n); - CHECK_THROW(test9_helper(opt), fail_fast); - } -#endif - - TEST(TestMaybeNullCasting) - { - MyDerived derived; - maybe_null p = &derived; - CHECK(p.present()); - - maybe_null q = p; - CHECK(q == p); - - maybe_null_dbg pdbg = &derived; - CHECK(pdbg.present()); - - maybe_null_dbg qdbg = pdbg; - CHECK(qdbg == pdbg); - -#ifdef CONFIRM_COMPILATION_ERRORS - maybe_null r = p; - maybe_null s = reinterpret_cast(p); -#endif - maybe_null_dbg t = reinterpret_cast(p.get()); - - CHECK_THROW((void)(void*)t.get(), fail_fast); - maybe_null_dbg u = reinterpret_cast(p.get()); - CHECK(u.present()); - CHECK((void*)p.get() == (void*)u.get()); - } - - TEST(TestMaybeNullArrow) - { - MyDerived derived; - maybe_null_dbg p = &derived; - - CHECK_THROW(p->foo(), fail_fast); - CHECK(p.present()); - CHECK(p->foo()); - - maybe_null q = p; - CHECK(q.present()); - CHECK(q->foo()); - } - - TEST(TestMaybeNullCompare) - { - int i1 = 1; - int i2 = 2; - - maybe_null_dbg p1 = &i1; - maybe_null_dbg p1_2 = &i1; - maybe_null_dbg p2 = &i2; - - CHECK_THROW(p1.get(), fail_fast); - CHECK_THROW(p1_2.get(), fail_fast); - CHECK_THROW(p2.get(), fail_fast); - - CHECK(p1 != p2); - CHECK(!(p1 == p2)); - CHECK(p1 == p1); - CHECK(p1 == p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - CHECK(p1_2.get() != nullptr); - CHECK(p2.get() != nullptr); - } - - TEST(TestMaybeNullCopy) - { - int i1 = 1; - int i2 = 2; - - maybe_null_dbg p1 = &i1; - maybe_null_dbg p1_2 = &i1; - maybe_null_dbg p2 = &i2; - - CHECK(p1 != p2); - CHECK(p1 == p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - CHECK(p2.get() != nullptr); - - p1 = p2; - - // Make sure we now throw - CHECK_THROW(p1.get(), fail_fast); - - CHECK(p1 == p2); - CHECK(p1 != p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - } - - TEST(TestMaybeNullAssignmentOps) - { - MyBase base; - MyDerived derived; - Unrelated unrelated; - - not_null nnBase(&base); - not_null nnDerived(&derived); - not_null nnUnrelated(&unrelated); - - maybe_null_ret mnBase_ret1(&base), mnBase_ret2; - mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret - mnBase_ret2 = nnBase; // maybe_null_ret = not_null - - maybe_null_ret mnDerived_ret(&derived); - mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret - mnBase_ret1 = &derived; // maybe_null_ret = U; - mnBase_ret1 = nnDerived; // maybe_null_ret = not_null - - maybe_null_ret mnUnrelated_ret; - mnUnrelated_ret = &unrelated; // maybe_null_ret = T - - maybe_null_dbg mnBase_dbg1(&base), mnBase_dbg2; - mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null - - maybe_null_dbg mnDerived_dbg(&derived); - mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg1 = &derived; // maybe_null_dbg = U; - mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null - - maybe_null_dbg mnUnrelated_dbg; - mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T - } -} - -int main(int, const char *[]) -{ - return UnitTest::RunAllTests(); -} -- cgit v1.2.3 From db38497d05887e8c901504fa09d71042ed8bb3c0 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 5 Oct 2015 12:34:23 -0700 Subject: Removed coordinate_facade class --- include/array_view.h | 493 ++++++++++++++++++++------------------------- tests/array_view_tests.cpp | 23 +-- 2 files changed, 221 insertions(+), 295 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 046cbf8..aa1b4e5 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -73,180 +73,6 @@ namespace details static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); }; - - template - class coordinate_facade - { - static_assert(std::is_integral::value - && sizeof(ValueType) <= sizeof(size_t), "ValueType must be an integral type!"); - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class coordinate_facade; - public: - using reference = ValueType&; - using const_reference = const ValueType&; - using value_type = ValueType; - static const size_t rank = Rank; - constexpr coordinate_facade() noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - } - constexpr coordinate_facade(const value_type(&values)[rank]) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - for (size_t i = 0; i < rank; ++i) - elems[i] = values[i]; - } - constexpr coordinate_facade(value_type e0) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - static_assert(rank == 1, "This constructor can only be used with rank == 1."); - elems[0] = e0; - } - // Preconditions: il.size() == rank - constexpr coordinate_facade(std::initializer_list il) - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); - for (size_t i = 0; i < rank; ++i) - { - elems[i] = begin(il)[i]; - } - } - - constexpr coordinate_facade(const coordinate_facade & other) = default; - - template - constexpr coordinate_facade(const coordinate_facade & other) - { - for (size_t i = 0; i < rank; ++i) - { - fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); - elems[i] = static_cast(other.elems[i]); - } - } - protected: - coordinate_facade& operator=(const coordinate_facade& rhs) = default; - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - constexpr bool operator==(const ConcreteType& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - constexpr bool operator!=(const ConcreteType& rhs) const noexcept - { - return !(to_concrete() == rhs); - } - constexpr ConcreteType operator+() const noexcept - { - return to_concrete(); - } - constexpr ConcreteType operator-() const - { - ConcreteType ret = to_concrete(); - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - constexpr ConcreteType operator+(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret += rhs; - return ret; - } - constexpr ConcreteType operator-(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret -= rhs; - return ret; - } - constexpr ConcreteType& operator+=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] += rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator-=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] -= rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator++() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ++elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator++(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - ++(*this); - return ret; - } - constexpr ConcreteType& operator--() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - --elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator--(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - --(*this); - return ret; - } - constexpr ConcreteType operator*(value_type v) const - { - ConcreteType ret = to_concrete(); - ret *= v; - return ret; - } - constexpr ConcreteType operator/(value_type v) const - { - ConcreteType ret = to_concrete(); - ret /= v; - return ret; - } - friend constexpr ConcreteType operator*(value_type v, const ConcreteType& rhs) - { - return rhs * v; - } - constexpr ConcreteType& operator*=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] *= v; - return to_concrete(); - } - constexpr ConcreteType& operator/=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] /= v; - return to_concrete(); - } - value_type elems[rank] = {}; - private: - constexpr const ConcreteType& to_concrete() const noexcept - { - return static_cast(*this); - } - constexpr ConcreteType& to_concrete() noexcept - { - return static_cast(*this); - } - }; template class arrow_proxy { @@ -268,47 +94,160 @@ namespace details } template -class index : private details::coordinate_facade, ValueType, Rank> +class index final { - using Base = details::coordinate_facade, ValueType, Rank>; - friend Base; + static_assert(std::is_integral::value, "ValueType must be an integral type!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); + template friend class index; + public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using value_type = typename Base::value_type; - constexpr index() noexcept : Base(){} - constexpr index(const value_type (&values)[rank]) noexcept : Base(values) {} - constexpr index(std::initializer_list il) : Base(il) {} + static const size_t rank = Rank; + using value_type = std::remove_reference_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index(const index &) = default; + constexpr index(const value_type(&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + // Preconditions: il.size() == rank + constexpr index(std::initializer_list il) noexcept + { + fail_fast_assert(il.size() == Rank, "The size of the initializer list must match the rank of the array"); + std::copy(begin(il), end(il), elems); + } + + constexpr index(const index& other) noexcept = default; + + // copy from index over smaller domain template - constexpr index(const index &other) : Base(other) + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index>::type& other) noexcept { - } - constexpr static index shift_left(const index& other) noexcept + std::copy(other.elems, other.elems + Rank, elems); + } + + // copy from index over larger domain + template + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index>::type& other) noexcept { - value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); - return index(arr); + for (size_t i = 0; i < Rank; ++i) + { + fail_fast_assert(other.elems[i] <= static_cast(SizeTypeTraits::max_value)); + elems[i] = static_cast(other.elems[i]); + } } - using Base::operator[]; - using Base::operator==; - using Base::operator!=; - using Base::operator+; - using Base::operator-; - using Base::operator+=; - using Base::operator-=; - using Base::operator++; - using Base::operator--; - using Base::operator*; - using Base::operator/; - using Base::operator*=; - using Base::operator/=; + constexpr static index shift_left(const index& other) noexcept + { + value_type(&arr)[Rank] = (value_type(&)[Rank])(*(other.elems + 1)); + return index(arr); + } + + constexpr static index zero() noexcept + { + value_type zero[Rank] = {}; + return index(zero); + } + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept + { + return !(this == rhs); + } + + constexpr index operator+() const noexcept + { + return *this; + } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend static constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } +private: + value_type elems[Rank] = {}; }; template @@ -316,51 +255,60 @@ class index<1, ValueType> { template friend class index; + public: static const size_t rank = 1; - using reference = ValueType&; - using const_reference = const ValueType&; - using size_type = ValueType; - using value_type = ValueType; + using value_type = std::remove_reference_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index() noexcept : value(0) - { - } constexpr index(value_type e0) noexcept : value(e0) - { - } + {} + constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - { - } + {} + // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) + constexpr index(std::initializer_list il) noexcept { fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); value = begin(il)[0]; } - constexpr index(const index &) = default; + constexpr index(const index &) noexcept = default; template - constexpr index(const index<1, OtherValueType> & other) + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept { - fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); value = static_cast(other.value); } - constexpr static index shift_left(const index& other) noexcept + template + constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + { + fail_fast_assert(other.value <= static_cast(SizeTypeTraits::max_value)); + value = static_cast(other.value); + } + + constexpr static index shift_left(const index<2, value_type>& other) noexcept { return other.elems[1]; } - // Preconditions: component_idx < rank - constexpr reference operator[](size_type component_idx) noexcept + + constexpr static index zero() noexcept + { + return 0; + } + + // Preconditions: component_idx < 1 + constexpr reference operator[](value_type component_idx) noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); return value; } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_type component_idx) const noexcept + // Preconditions: component_idx < 1 + constexpr const_reference operator[](value_type component_idx) const noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); (void)(component_idx); @@ -440,9 +388,9 @@ public: value /= v; return *this; } - friend constexpr index operator*(value_type v, const index& rhs) noexcept + friend static constexpr index operator*(value_type v, const index& rhs) noexcept { - return index(rhs * v); + return{ rhs * v }; } private: value_type value; @@ -855,9 +803,9 @@ public: constexpr index_type index_bounds() const noexcept { - index_type extents; + size_type extents[rank]; m_ranges.serialize(extents); - return extents; + return{ extents }; } template @@ -874,32 +822,28 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this); + return const_iterator(*this, index_type::zero()); } constexpr const_iterator end() const noexcept { - index_type boundary; - m_ranges.serialize(boundary); return const_iterator(*this, this->index_bounds()); } }; template -class strided_bounds : private details::coordinate_facade, SizeType, Rank> +class strided_bounds { - using Base = details::coordinate_facade, SizeType, Rank>; - friend Base; template friend class strided_bounds; public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using difference_type = typename Base::value_type; - using value_type = typename Base::value_type; + static const size_t rank = Rank; + using reference = typename SizeType&; + using const_reference = typename const SizeType&; + using size_type = typename SizeType; + using difference_type = typename SizeType; + using value_type = typename SizeType; using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; @@ -907,57 +851,52 @@ public: static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds &) = default; + constexpr strided_bounds(const strided_bounds &) noexcept = default; template - constexpr strided_bounds(const strided_bounds &other) - : Base(other), m_strides(other.strides) - { - } - - constexpr strided_bounds(const index_type &extents, const index_type &strides) - : m_strides(strides) - { - for (size_t i = 0; i < rank; i++) - Base::elems[i] = extents[i]; - } - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : Base(values), m_strides(std::move(strides)) - { - } + constexpr strided_bounds(const strided_bounds &other) noexcept + : m_extents(other.extents), m_strides(other.strides) + {} + constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept + : m_extents(extents), m_strides(strides) + {} constexpr index_type strides() const noexcept - { - return m_strides; + { + return m_strides; } constexpr size_type total_size() const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; ++i) - ret += (Base::elems[i] - 1) * m_strides[i]; + { + ret += (m_extents[i] - 1) * m_strides[i]; + } return ret + 1; } constexpr size_type size() const noexcept { size_type ret = 1; for (size_t i = 0; i < rank; ++i) - ret *= Base::elems[i]; + { + ret *= m_extents[i]; + } return ret; } constexpr bool contains(const index_type& idx) const noexcept { for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= Base::elems[i]) + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; } return true; } - constexpr size_type linearize(const index_type & idx) const + constexpr size_type linearize(const index_type & idx) const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; i++) { - fail_fast_assert(idx[i] < Base::elems[i], "index is out of bounds of the array"); + fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); ret += idx[i] * m_strides[i]; } return ret; @@ -969,27 +908,28 @@ public: template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; + return{ sliced_type::index_type::shift_left(m_extents), sliced_type::index_type::shift_left(m_strides) }; } template constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); - return Base::elems[Dim]; + return m_extents[Dim]; } constexpr index_type index_bounds() const noexcept { - return index_type(Base::elems); + return m_extents; } const_iterator begin() const noexcept { - return const_iterator{ *this }; + return const_iterator{ *this, index_type::zero() }; } const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } private: + index_type m_extents; index_type m_strides; }; @@ -1017,9 +957,9 @@ public: using typename Base::difference_type; using typename Base::value_type; using index_type = value_type; - using index_size_type = typename IndexType::size_type; + using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) noexcept + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept : boundary(bnd.index_bounds()) , curr( std::move(curr) ) { @@ -1210,7 +1150,7 @@ public: using typename Base::difference_type; using typename Base::value_type; using index_type = value_type; - using index_size_type = typename index_type::size_type; + using index_size_type = typename index_type::value_type; template explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) noexcept @@ -1327,11 +1267,14 @@ namespace details constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); - typename Bounds::index_type stride; - stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) - stride[i-1] = stride[i] * extents[i]; - return stride; + Bounds::size_type stride[Bounds::rank]; + + stride[Bounds::rank - 1] = 1; + for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) + { + stride[i - 1] = stride[i] * extents[i]; + } + return{ stride }; } template diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 918df9e..ec10bbd 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -552,26 +552,6 @@ SUITE(array_view_tests) CHECK_THROW(av.section(5, 5), fail_fast); } - { - // zero stride - strided_array_view sav{ av, {{4}, {}} }; - CHECK(sav[0] == 0); - CHECK(sav[3] == 0); - CHECK_THROW(sav[4], fail_fast); - } - - { - // zero extent - strided_array_view sav{ av,{ {},{1} } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // zero extent and stride - strided_array_view sav{ av,{ {},{} } }; - CHECK_THROW(sav[0], fail_fast); - } - { // strided array ctor with matching strided bounds strided_array_view sav{ arr,{ 4, 1 } }; @@ -627,6 +607,9 @@ SUITE(array_view_tests) #ifdef CONFIRM_COMPILATION_ERRORS { + strided_array_view sav{ av,{ { 4 },{} } }; + strided_array_view sav{ av,{ {},{ 1 } } }; + strided_array_view sav{ av,{ {},{} } }; strided_array_view sav0{ av.data(), { 3, 2 } }; strided_array_view sav1{ arr, { 1 } }; strided_array_view sav2{ arr, { 1,1,1 } }; -- cgit v1.2.3 From 546f8cc1306ec69cc3a8f292785658da8caf157a Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 5 Oct 2015 21:04:56 -0700 Subject: Added tests for index size_type conversions --- include/array_view.h | 106 +++++++++++------------ tests/array_view_tests.cpp | 209 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 243 insertions(+), 72 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index aa1b4e5..98bbadf 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -70,7 +70,7 @@ namespace details template struct SizeTypeTraits { - static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + static const SizeType max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); }; template @@ -108,6 +108,9 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; + constexpr index() noexcept + {} + constexpr index(const value_type(&values)[Rank]) noexcept { std::copy(values, values + Rank, elems); @@ -120,36 +123,29 @@ public: std::copy(begin(il), end(il), elems); } - constexpr index(const index& other) noexcept = default; + constexpr index(const index& other) noexcept = default; // copy from index over smaller domain - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index>::type& other) noexcept + template ::max_value <= details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& other) noexcept { std::copy(other.elems, other.elems + Rank, elems); } // copy from index over larger domain - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index>::type& other) noexcept - { - for (size_t i = 0; i < Rank; ++i) - { - fail_fast_assert(other.elems[i] <= static_cast(SizeTypeTraits::max_value)); - elems[i] = static_cast(other.elems[i]); - } - } - - constexpr static index shift_left(const index& other) noexcept + template ::max_value > details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& other, void* ptr = 0) noexcept { - value_type(&arr)[Rank] = (value_type(&)[Rank])(*(other.elems + 1)); - return index(arr); - } + bool ok = std::accumulate(other.elems, other.elems + Rank, true, + [&](bool b, OtherValueType val) { return b && (val <= static_cast(details::SizeTypeTraits::max_value)); } + ); - constexpr static index zero() noexcept - { - value_type zero[Rank] = {}; - return index(zero); + fail_fast_assert(ok, "other value must fit in the new domain"); + std::transform(other.elems, other.elems + rank, elems, [&](OtherValueType val) { return static_cast(val); }); } constexpr index& operator=(const index& rhs) noexcept = default; @@ -230,7 +226,7 @@ public: return ret; } - friend static constexpr index operator*(value_type v, const index& rhs) noexcept + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } @@ -246,6 +242,7 @@ public: std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); return *this; } + private: value_type elems[Rank] = {}; }; @@ -262,44 +259,34 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; - constexpr index(value_type e0) noexcept : value(e0) + constexpr index() noexcept : value(0) {} - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) + constexpr index(value_type e) noexcept : value(e) {} - // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) noexcept - { - fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); - value = begin(il)[0]; - } + constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) + {} constexpr index(const index &) noexcept = default; - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value <= details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + template ::max_value <= details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index<1, OtherValueType>& other) noexcept { value = static_cast(other.value); } - template - constexpr index(typename std::enable_if_t<(details::SizeTypeTraits::max_value > details::SizeTypeTraits::max_value), const index<1, OtherValueType>>::type& other) noexcept + template ::max_value > details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index<1, OtherValueType>& other, void* ptr=0) noexcept { - fail_fast_assert(other.value <= static_cast(SizeTypeTraits::max_value)); + fail_fast_assert(other.value <= static_cast(details::SizeTypeTraits::max_value)); value = static_cast(other.value); } - constexpr static index shift_left(const index<2, value_type>& other) noexcept - { - return other.elems[1]; - } - - constexpr static index zero() noexcept - { - return 0; - } - // Preconditions: component_idx < 1 constexpr reference operator[](value_type component_idx) noexcept { @@ -388,7 +375,7 @@ public: value /= v; return *this; } - friend static constexpr index operator*(value_type v, const index& rhs) noexcept + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return{ rhs * v }; } @@ -822,7 +809,7 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this, index_type::zero()); + return const_iterator(*this); } constexpr const_iterator end() const noexcept @@ -908,7 +895,7 @@ public: template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ sliced_type::index_type::shift_left(m_extents), sliced_type::index_type::shift_left(m_strides) }; + return{ details::shift_left(m_extents), details::shift_left(m_strides) }; } template constexpr size_type extent() const noexcept @@ -922,7 +909,7 @@ public: } const_iterator begin() const noexcept { - return const_iterator{ *this, index_type::zero() }; + return const_iterator{ *this }; } const_iterator end() const noexcept { @@ -959,9 +946,9 @@ public: using index_type = value_type; using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + explicit bounds_iterator(const Bounds& bnd, value_type curr = value_type{}) noexcept : boundary(bnd.index_bounds()) - , curr( std::move(curr) ) + , curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } @@ -1270,13 +1257,24 @@ namespace details Bounds::size_type stride[Bounds::rank]; stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) + for (size_t i = 1; i < Bounds::rank; ++i) { - stride[i - 1] = stride[i] * extents[i]; + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; } return{ stride }; } + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret; + for (size_t i = 0; i < Rank - 1; ++i) + { + ret[i] = other[i + 1]; + } + return ret; + } + template void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) { diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index ec10bbd..a56d0f2 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -552,6 +552,26 @@ SUITE(array_view_tests) CHECK_THROW(av.section(5, 5), fail_fast); } + { + // zero stride + strided_array_view sav{ av,{ { 4 },{} } }; + CHECK(sav[0] == 0); + CHECK(sav[3] == 0); + CHECK_THROW(sav[4], fail_fast); + } + + { + // zero extent + strided_array_view sav{ av,{ {},{ 1 } } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // zero extent and stride + strided_array_view sav{ av,{ {},{} } }; + CHECK_THROW(sav[0], fail_fast); + } + { // strided array ctor with matching strided bounds strided_array_view sav{ arr,{ 4, 1 } }; @@ -607,9 +627,6 @@ SUITE(array_view_tests) #ifdef CONFIRM_COMPILATION_ERRORS { - strided_array_view sav{ av,{ { 4 },{} } }; - strided_array_view sav{ av,{ {},{ 1 } } }; - strided_array_view sav{ av,{ {},{} } }; strided_array_view sav0{ av.data(), { 3, 2 } }; strided_array_view sav1{ arr, { 1 } }; strided_array_view sav2{ arr, { 1,1,1 } }; @@ -618,27 +635,26 @@ SUITE(array_view_tests) strided_array_view sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; strided_array_view sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; strided_array_view sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; - } -#endif - - { - // stride initializer list size should match the rank of the array - CHECK_THROW((index<1>{ 0,1 }), fail_fast); - CHECK_THROW((strided_array_view{ arr, {1, {1,1}} }), fail_fast); + + index<1> index{ 0, 1 }; + strided_array_view sav8{ arr,{ 1,{ 1,1 } } }; #ifdef _MSC_VER - CHECK_THROW((strided_array_view{ arr, {{1,1 }, {1,1}} }), fail_fast); + strided_array_view sav9{ arr,{ { 1,1 },{ 1,1 } } }; #endif - CHECK_THROW((strided_array_view{ av, {1, {1,1}} }), fail_fast); + strided_array_view sav10{ av,{ 1,{ 1,1 } } }; #ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av, {{1,1 }, {1,1}} }), fail_fast); -#endif + strided_array_view sav11{ av,{ { 1,1 },{ 1,1 } } }; +#endif + } +#endif + + { CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); #ifdef _MSC_VER CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); #endif } - } TEST(strided_array_view_type_conversion) @@ -856,7 +872,18 @@ SUITE(array_view_tests) } { - index<2> k = index<2>::shift_left(i); + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); CHECK(i[0] == 0); CHECK(i[1] == 1); @@ -972,7 +999,7 @@ SUITE(array_view_tests) auto bounds = strided_bounds<1>({ length }, { 2 }); #else auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 }); -#endif +#endif strided_array_view strided(&av.data()[1], av.size() - 1, bounds); CHECK(strided.size() == length); @@ -1033,7 +1060,7 @@ SUITE(array_view_tests) for (unsigned int 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]); + CHECK(section[idx] == expected[2 * i + 2 * j + k]); } } @@ -1151,6 +1178,152 @@ SUITE(array_view_tests) } + template + index Convert(index index) + { + return{ index }; + } + + TEST(DomainConverters) + { + // to smaller + { + index<2, int> int_index{ 0,1 }; + index<2, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + CHECK(short_index[1] == 1); + } + + // to smaller (failure) + { + index<2, int> big_int_index{ INT_MAX, 1 }; + CHECK_THROW((Convert<2,int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + CHECK(uint_index[1] == 1); + } + + // to same, sign mismatch, reversed + { + index<2, unsigned int> uint_index{ 0,1 }; + index<2, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + CHECK(int_index[1] == 1); + } + + // to smaller, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + CHECK(ushort_index[1] == 1); + } + + // to bigger + { + index<2, int> int_index{ 0,1 }; + index<2, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + CHECK(longlong_index[1] == 1); + } + + // to bigger with max index + { + index<2, int> big_int_index{ INT_MAX, 1 }; + index<2, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == INT_MAX); + CHECK(longlong_index[1] == 1); + } + + // to bigger, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + CHECK(ulonglong_index[1] == 1); + } + + } + + TEST(DomainConvertersRank1) + { + // to smaller + { + index<1, int> int_index{ 0 }; + index<1, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + } + + // to smaller (failure) + { + index<1, int> big_int_index{ INT_MAX }; + + CHECK_THROW((Convert<1, int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + } + + // to same, sign mismatch, reversed + { + index<1, unsigned int> uint_index{ 0 }; + index<1, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + } + + // to smaller, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + } + + // to bigger + { + index<1, int> int_index{ 0 }; + index<1, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + } + + // to bigger with max index + { + index<1, int> big_int_index{ INT_MAX }; + index<1, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == INT_MAX); + } + + // to bigger, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + } + + } + TEST(constructors) { array_view av(nullptr); -- cgit v1.2.3 From fdf864347150a108a33a467bc7ba3efd1ad78b2f Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 14 Oct 2015 10:46:22 -0700 Subject: Fixes for gcc --- include/array_view.h | 36 ++++++++++++++++++------------------ tests/array_view_tests.cpp | 13 +++++++------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 98bbadf..3502076 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -686,6 +686,17 @@ namespace details { return TypeListIndexer(obj); } + + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret; + for (size_t i = 0; i < Rank - 1; ++i) + { + ret[i] = other[i + 1]; + } + return ret; + } } template @@ -790,7 +801,7 @@ public: constexpr index_type index_bounds() const noexcept { - size_type extents[rank]; + size_type extents[rank] = {}; m_ranges.serialize(extents); return{ extents }; } @@ -826,11 +837,11 @@ class strided_bounds public: static const size_t rank = Rank; - using reference = typename SizeType&; - using const_reference = typename const SizeType&; - using size_type = typename SizeType; - using difference_type = typename SizeType; - using value_type = typename SizeType; + using reference = SizeType&; + using const_reference = const SizeType&; + using size_type = SizeType; + using difference_type = SizeType; + using value_type = SizeType; using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; @@ -1254,7 +1265,7 @@ namespace details constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); - Bounds::size_type stride[Bounds::rank]; + typename Bounds::size_type stride[Bounds::rank] = {}; stride[Bounds::rank - 1] = 1; for (size_t i = 1; i < Bounds::rank; ++i) @@ -1264,17 +1275,6 @@ namespace details return{ stride }; } - template 1), typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret; - for (size_t i = 0; i < Rank - 1; ++i) - { - ret[i] = other[i + 1]; - } - return ret; - } - template void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) { diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index a56d0f2..f84e908 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1197,7 +1198,7 @@ SUITE(array_view_tests) // to smaller (failure) { - index<2, int> big_int_index{ INT_MAX, 1 }; + index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; CHECK_THROW((Convert<2,int, short int>(big_int_index)), fail_fast); } @@ -1239,10 +1240,10 @@ SUITE(array_view_tests) // to bigger with max index { - index<2, int> big_int_index{ INT_MAX, 1 }; + index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; index<2, long long> longlong_index{ big_int_index }; - CHECK(longlong_index[0] == INT_MAX); + CHECK(longlong_index[0] == std::numeric_limits::max()); CHECK(longlong_index[1] == 1); } @@ -1269,7 +1270,7 @@ SUITE(array_view_tests) // to smaller (failure) { - index<1, int> big_int_index{ INT_MAX }; + index<1, int> big_int_index{ std::numeric_limits::max() }; CHECK_THROW((Convert<1, int, short int>(big_int_index)), fail_fast); } @@ -1308,10 +1309,10 @@ SUITE(array_view_tests) // to bigger with max index { - index<1, int> big_int_index{ INT_MAX }; + index<1, int> big_int_index{ std::numeric_limits::max() }; index<1, long long> longlong_index{ big_int_index }; - CHECK(longlong_index[0] == INT_MAX); + CHECK(longlong_index[0] == std::numeric_limits::max()); } // to bigger, sign mismatch -- cgit v1.2.3 From f972b2d68c9a9d7ce151d93b3a3b3b2da9ecb6eb Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 15 Oct 2015 13:00:10 -0700 Subject: Adding g++-5 libraries to clang travis configuration to fix build break --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 524f1fb..3c64230 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ matrix: packages: - clang-3.6 - cmake + - g++-5 sources: &sources - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.6 -- cgit v1.2.3 From 2cdedda7e4d85f356b8ef66f2d6f7e778538ba00 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 15 Oct 2015 13:19:24 -0700 Subject: Adding missing include library to array_view.h --- include/array_view.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/array_view.h b/include/array_view.h index 3502076..c884c11 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From f45fedbec21f12b8a5b8ea6bfa18410327849261 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 15 Oct 2015 14:29:35 -0700 Subject: Partway through removing configurable SizeType. --- include/array_view.h | 782 +++++++++++++++++++++------------------------ include/string_view.h | 39 +-- tests/array_view_tests.cpp | 12 +- tests/bounds_tests.cpp | 36 +-- 4 files changed, 409 insertions(+), 460 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 046cbf8..1178e43 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -67,43 +67,38 @@ namespace gsl { */ namespace details { - template - struct SizeTypeTraits - { - static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); - }; - - - template + template class coordinate_facade { - static_assert(std::is_integral::value - && sizeof(ValueType) <= sizeof(size_t), "ValueType must be an integral type!"); static_assert(Rank > 0, "Rank must be greater than 0!"); - template + template friend class coordinate_facade; public: - using reference = ValueType&; - using const_reference = const ValueType&; - using value_type = ValueType; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using value_type = std::ptrdiff_t; static const size_t rank = Rank; + constexpr coordinate_facade() noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); } + constexpr coordinate_facade(const value_type(&values)[rank]) noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); for (size_t i = 0; i < rank; ++i) elems[i] = values[i]; } + constexpr coordinate_facade(value_type e0) noexcept { static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); static_assert(rank == 1, "This constructor can only be used with rank == 1."); elems[0] = e0; } + // Preconditions: il.size() == rank constexpr coordinate_facade(std::initializer_list il) { @@ -117,15 +112,13 @@ namespace details constexpr coordinate_facade(const coordinate_facade & other) = default; - template - constexpr coordinate_facade(const coordinate_facade & other) + template + constexpr coordinate_facade(const coordinate_facade& other) { - for (size_t i = 0; i < rank; ++i) - { - fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); - elems[i] = static_cast(other.elems[i]); - } + for (size_t i = 0; i < rank; ++i) + elems[i] = other.elems[i]; } + protected: coordinate_facade& operator=(const coordinate_facade& rhs) = default; // Preconditions: component_idx < rank @@ -267,12 +260,12 @@ namespace details }; } -template -class index : private details::coordinate_facade, ValueType, Rank> +template +class index : private details::coordinate_facade, Rank> { - using Base = details::coordinate_facade, ValueType, Rank>; + using Base = details::coordinate_facade, Rank>; friend Base; - template + template friend class index; public: using Base::rank; @@ -286,11 +279,7 @@ public: constexpr index(const index &) = default; - template - constexpr index(const index &other) : Base(other) - { - } - constexpr static index shift_left(const index& other) noexcept + constexpr static index shift_left(const index& other) noexcept { value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); return index(arr); @@ -311,28 +300,26 @@ public: using Base::operator/=; }; -template -class index<1, ValueType> +template<> +class index<1> { - template - friend class index; public: static const size_t rank = 1; - using reference = ValueType&; - using const_reference = const ValueType&; - using size_type = ValueType; - using value_type = ValueType; + using size_type = std::ptrdiff_t; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = const std::ptrdiff_t&;//std::add_const_t>; - constexpr index() noexcept : value(0) - { - } + constexpr index() noexcept : value(0) + {} + constexpr index(value_type e0) noexcept : value(e0) - { - } - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - { - } - // Preconditions: il.size() == rank + {} + + constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) + {} + + // Preconditions: il.size() == rank constexpr index(std::initializer_list il) { fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); @@ -341,288 +328,239 @@ public: constexpr index(const index &) = default; - template - constexpr index(const index<1, OtherValueType> & other) - { - fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); - value = static_cast(other.value); - } - - constexpr static index shift_left(const index& other) noexcept + constexpr static index shift_left(const index& other) noexcept { return other.elems[1]; } + // Preconditions: component_idx < rank constexpr reference operator[](size_type component_idx) noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); return value; } + // Preconditions: component_idx < rank constexpr const_reference operator[](size_type component_idx) const noexcept { fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); return value; } + constexpr bool operator==(const index& rhs) const noexcept { return value == rhs.value; } + constexpr bool operator!=(const index& rhs) const noexcept { return !(*this == rhs); } - constexpr index operator+() const noexcept + + constexpr index operator+() const noexcept { return *this; } - constexpr index operator-() const noexcept + + constexpr index operator-() const noexcept { return index(-value); } - constexpr index operator+(const index& rhs) const noexcept + + constexpr index operator+(const index& rhs) const noexcept { return index(value + rhs.value); } - constexpr index operator-(const index& rhs) const noexcept + + constexpr index operator-(const index& rhs) const noexcept { return index(value - rhs.value); } - constexpr index& operator+=(const index& rhs) noexcept + + constexpr index& operator+=(const index& rhs) noexcept { value += rhs.value; return *this; } - constexpr index& operator-=(const index& rhs) noexcept + + constexpr index& operator-=(const index& rhs) noexcept { value -= rhs.value; return *this; } - constexpr index& operator++() noexcept + + constexpr index& operator++() noexcept { ++value; return *this; } - constexpr index operator++(int) noexcept + + constexpr index operator++(int) noexcept { index ret = *this; ++(*this); return ret; } - constexpr index& operator--() noexcept + + constexpr index& operator--() noexcept { --value; return *this; } - constexpr index operator--(int) noexcept + + constexpr index operator--(int) noexcept { index ret = *this; --(*this); return ret; } - constexpr index operator*(value_type v) const noexcept + + constexpr index operator*(value_type v) const noexcept { return index(value * v); } - constexpr index operator/(value_type v) const noexcept + + constexpr index operator/(value_type v) const noexcept { return index(value / v); } - constexpr index& operator*=(value_type v) noexcept + + constexpr index& operator*=(value_type v) noexcept { value *= v; return *this; } - constexpr index& operator/=(value_type v) noexcept + + constexpr index& operator/=(value_type v) noexcept { value /= v; return *this; } - friend constexpr index operator*(value_type v, const index& rhs) noexcept + + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return index(rhs * v); } + private: value_type value; }; -#ifndef _MSC_VER - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const noexcept - { - return static_cast(-1); - } - - template ::value>> - constexpr bool operator ==(T other) const noexcept - { - return static_cast(-1) == other; - } - - template ::value>> - constexpr bool operator !=(T other) const noexcept - { - return static_cast(-1) != other; - } - -}; - -template ::value>> -constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right == left; -} - -template ::value>> -constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const char dynamic_range = -1; -#endif +const std::ptrdiff_t dynamic_range = -1; struct generalized_mapping_tag {}; struct contiguous_mapping_tag : generalized_mapping_tag {}; namespace details { - template - struct StaticSizeHelperImpl - { - static_assert(static_cast(Fact1) * static_cast(Fact2) <= SizeTypeTraits::max_value, "Value out of the range of SizeType"); - static const SizeType value = Fact1 * Fact2; - }; - template - struct StaticSizeHelperImpl - { - static const SizeType value = ConstBound; - }; - - template - struct StaticSizeHelperImpl - { - static const SizeType value = ConstBound; - }; - - template - struct StaticSizeHelperImpl - { - static const SizeType value = static_cast(ConstBound); - }; - - template - struct StaticSizeHelper - { - static const SizeType value = StaticSizeHelperImpl(Fact1), static_cast(Fact2), static_cast(dynamic_range)>::value; - }; - - - template + template struct LessThan { static const bool value = Left < Right; }; - template + template struct BoundsRanges { - static const size_t Depth = 0; - static const size_t DynamicNum = 0; - static const SizeType CurrentRange = 1; - static const SizeType TotalSize = 1; - - BoundsRanges (const BoundsRanges &) = default; - - // TODO : following signature is for work around VS bug - template - BoundsRanges (const OtherType &, bool /* firstLevel */) {} - BoundsRanges(const SizeType * const) { } + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const size_type* const) { } BoundsRanges() = default; template - void serialize(T &) const { - } + void serialize(T&) const + {} + template - SizeType linearize(const T &) const { + size_type linearize(const T&) const + { return 0; } + template - ptrdiff_t contains(const T &) const { + bool contains(const T&) const + { return 0; } - size_t totalSize() const noexcept { + size_type totalSize() const noexcept + { return TotalSize; } - bool operator == (const BoundsRanges &) const noexcept + bool operator==(const BoundsRanges&) const noexcept { return true; } }; - template - struct BoundsRanges : BoundsRanges{ - using Base = BoundsRanges ; + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + using size_type = Base::size_type; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum + 1; - static const SizeType CurrentRange = dynamic_range; - static const SizeType TotalSize = dynamic_range; - const SizeType m_bound; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; - BoundsRanges (const BoundsRanges &) = default; - BoundsRanges(const SizeType * const arr) : Base(arr + 1), m_bound(static_cast(*arr * this->Base::totalSize())) + BoundsRanges (const BoundsRanges&) = default; + + BoundsRanges(const size_type* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { fail_fast_assert(0 <= *arr); - fail_fast_assert(*arr * this->Base::totalSize() <= details::SizeTypeTraits::max_value); } + BoundsRanges() : m_bound(0) {} - template - BoundsRanges(const BoundsRanges &other, bool /* firstLevel */ = true) : - Base(static_cast&>(other), false), m_bound (static_cast(other.totalSize())) - { - } + template + BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : + Base(static_cast&>(other), false), m_bound(other.totalSize()) + {} template - void serialize(T & arr) const { + void serialize(T& arr) const + { arr[Dim] = elementNum(); this->Base::template serialize(arr); } + template - SizeType linearize(const T & arr) const { - const size_t index = this->Base::totalSize() * arr[Dim]; - fail_fast_assert(index < static_cast(m_bound)); - return static_cast(index) + this->Base::template linearize(arr); + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + fail_fast_assert(index < m_bound); + return index + this->Base::template linearize(arr); } template - ptrdiff_t contains(const T & arr) const { + size_type contains(const T & arr) const + { const ptrdiff_t last = this->Base::template contains(arr); if (last == -1) return -1; const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return static_cast(cur) < static_cast(m_bound) ? cur + last : -1; + return cur < m_bound ? cur + last : -1; } - size_t totalSize() const noexcept { + size_type totalSize() const noexcept + { return m_bound; } - SizeType elementNum() const noexcept { - return static_cast(totalSize() / this->Base::totalSize()); + size_type elementNum() const noexcept + { + return totalSize() / this->Base::totalSize(); } - SizeType elementNum(size_t dim) const noexcept{ + size_type elementNum(size_t dim) const noexcept + { if (dim > 0) return this->Base::elementNum(dim - 1); else @@ -631,67 +569,75 @@ namespace details bool operator == (const BoundsRanges & rhs) const noexcept { - return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); } }; - template - struct BoundsRanges : BoundsRanges{ - using Base = BoundsRanges ; + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges ; + using size_type = Base::size_type; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum; - static const SizeType CurrentRange = static_cast(CurRange); - static const SizeType TotalSize = StaticSizeHelper::value; - static_assert (CurRange <= SizeTypeTraits::max_value, "CurRange must be smaller than SizeType limits"); + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = CurrentRange; + static_assert (CurRange <= PTRDIFF_MAX, "CurRange must be smaller than std::ptrdiff_t limits"); - BoundsRanges (const BoundsRanges &) = default; - BoundsRanges(const SizeType * const arr) : Base(arr) { } + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const size_type* const arr) : Base(arr) { } BoundsRanges() = default; - template - BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : Base(static_cast&>(other), false) + template + BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) { fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); } template - void serialize(T & arr) const { + void serialize(T& arr) const + { arr[Dim] = elementNum(); this->Base::template serialize(arr); } template - SizeType linearize(const T & arr) const { + size_type linearize(const T& arr) const + { fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); - return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); + return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); } template - ptrdiff_t contains(const T & arr) const { - if (static_cast(arr[Dim]) >= CurrentRange) + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; - const ptrdiff_t last = this->Base::template contains(arr); + const size_type last = this->Base::template contains(arr); if (last == -1) return -1; - return static_cast(this->Base::totalSize() * arr[Dim]) + last; + return this->Base::totalSize() * arr[Dim] + last; } - size_t totalSize() const noexcept{ + size_type totalSize() const noexcept + { return CurrentRange * this->Base::totalSize(); } - SizeType elementNum() const noexcept{ + size_type elementNum() const noexcept + { return CurrentRange; } - SizeType elementNum(size_t dim) const noexcept{ + size_type elementNum(size_t dim) const noexcept + { if (dim > 0) return this->Base::elementNum(dim - 1); else return elementNum(); } - bool operator == (const BoundsRanges & rhs) const noexcept + bool operator== (const BoundsRanges& rhs) const noexcept { return static_cast(*this) == static_cast(rhs); } @@ -700,7 +646,6 @@ namespace details template struct BoundsRangeConvertible2; - // TODO: I have to rewrite BoundsRangeConvertible into following way to workaround VS 2013 bugs template > auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; @@ -756,56 +701,55 @@ namespace details template class bounds_iterator; -template -class static_bounds { +template +class static_bounds +{ public: - static_bounds(const details::BoundsRanges &) { + static_bounds(const details::BoundsRanges&) { } }; -template -class static_bounds +template +class static_bounds { - using MyRanges = details::BoundsRanges ; - static_assert(std::is_integral::value - && details::SizeTypeTraits::max_value <= SIZE_MAX, "SizeType must be an integral type and its numeric limits must be smaller than SIZE_MAX"); + using MyRanges = details::BoundsRanges; MyRanges m_ranges; - constexpr static_bounds(const MyRanges & range) : m_ranges(range) { } + constexpr static_bounds(const MyRanges& range) : m_ranges(range) + {} - template + template friend class static_bounds; + public: static const size_t rank = MyRanges::Depth; static const size_t dynamic_rank = MyRanges::DynamicNum; - static const SizeType static_size = static_cast(MyRanges::TotalSize); + static const std::ptrdiff_t static_size = MyRanges::TotalSize; - using size_type = SizeType; - using index_type = index; + using size_type = std::ptrdiff_t; + using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; - using difference_type = ptrdiff_t; - using sliced_type = static_bounds; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; public: - constexpr static_bounds(const static_bounds &) = default; + constexpr static_bounds(const static_bounds&) = default; - template , details::BoundsRanges >::value>> - constexpr static_bounds(const static_bounds &other): - m_ranges(other.m_ranges) - { - } + template , details::BoundsRanges >::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + {} constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); - fail_fast_assert(m_ranges.totalSize() <= details::SizeTypeTraits::max_value, "Size of the range is larger than the max element of the size type"); + fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); } constexpr static_bounds() = default; - constexpr static_bounds & operator = (const static_bounds & otherBounds) + constexpr static_bounds& operator=(const static_bounds& otherBounds) { new(&m_ranges) MyRanges (otherBounds.m_ranges); return *this; @@ -813,7 +757,7 @@ public: constexpr sliced_type slice() const noexcept { - return sliced_type{static_cast &>(m_ranges)}; + return sliced_type{static_cast &>(m_ranges)}; } constexpr size_type stride() const noexcept @@ -823,12 +767,12 @@ public: constexpr size_type size() const noexcept { - return static_cast(m_ranges.totalSize()); + return m_ranges.totalSize(); } constexpr size_type total_size() const noexcept { - return static_cast(m_ranges.totalSize()); + return m_ranges.totalSize(); } constexpr size_type linearize(const index_type & idx) const @@ -860,14 +804,14 @@ public: return extents; } - template - constexpr bool operator == (const static_bounds & rhs) const noexcept + template + constexpr bool operator == (const static_bounds& rhs) const noexcept { return this->size() == rhs.size(); } - template - constexpr bool operator != (const static_bounds & rhs) const noexcept + template + constexpr bool operator != (const static_bounds& rhs) const noexcept { return !(*this == rhs); } @@ -885,12 +829,13 @@ public: } }; -template -class strided_bounds : private details::coordinate_facade, SizeType, Rank> +template +class strided_bounds : private details::coordinate_facade, Rank> { - using Base = details::coordinate_facade, SizeType, Rank>; + using Base = details::coordinate_facade, Rank>; friend Base; - template + + template friend class strided_bounds; public: @@ -900,35 +845,31 @@ public: using size_type = typename Base::value_type; using difference_type = typename Base::value_type; using value_type = typename Base::value_type; - using index_type = index; + using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; static const int dynamic_rank = rank; - static const size_t static_size = dynamic_range; + static const std::ptrdiff_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; constexpr strided_bounds(const strided_bounds &) = default; - template - constexpr strided_bounds(const strided_bounds &other) - : Base(other), m_strides(other.strides) - { - } - - constexpr strided_bounds(const index_type &extents, const index_type &strides) - : m_strides(strides) + constexpr strided_bounds(const index_type& extents, const index_type& strides) + : m_strides(strides) { for (size_t i = 0; i < rank; i++) Base::elems[i] = extents[i]; } - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : Base(values), m_strides(std::move(strides)) - { - } + + constexpr strided_bounds(const value_type(&values)[rank], index_type strides) + : Base(values), m_strides(std::move(strides)) + {} + constexpr index_type strides() const noexcept { return m_strides; } + constexpr size_type total_size() const noexcept { size_type ret = 0; @@ -936,14 +877,16 @@ public: ret += (Base::elems[i] - 1) * m_strides[i]; return ret + 1; } - constexpr size_type size() const noexcept + + constexpr size_type size() const noexcept { size_type ret = 1; for (size_t i = 0; i < rank; ++i) ret *= Base::elems[i]; return ret; } - constexpr bool contains(const index_type& idx) const noexcept + + constexpr bool contains(const index_type& idx) const noexcept { for (size_t i = 0; i < rank; ++i) { @@ -952,7 +895,8 @@ public: } return true; } - constexpr size_type linearize(const index_type & idx) const + + constexpr size_type linearize(const index_type & idx) const { size_type ret = 0; for (size_t i = 0; i < rank; i++) @@ -962,43 +906,50 @@ public: } return ret; } - constexpr size_type stride() const noexcept + + constexpr size_type stride() const noexcept { return m_strides[0]; } - template 1), typename Ret = std::enable_if_t> + + template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; } - template + + template constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); return Base::elems[Dim]; } - constexpr index_type index_bounds() const noexcept + + constexpr index_type index_bounds() const noexcept { return index_type(Base::elems); } - const_iterator begin() const noexcept + + const_iterator begin() const noexcept { return const_iterator{ *this }; } - const_iterator end() const noexcept + + const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } + private: index_type m_strides; }; template struct is_bounds : std::integral_constant {}; -template -struct is_bounds> : std::integral_constant {}; -template -struct is_bounds> : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; template class bounds_iterator @@ -1194,26 +1145,26 @@ private: value_type curr; }; -template -class bounds_iterator> +template <> +class bounds_iterator> : public std::iterator, + index<1>, ptrdiff_t, - const details::arrow_proxy>, - const index<1, SizeType>> + const details::arrow_proxy>, + const index<1>> { - using Base = std::iterator, ptrdiff_t, const details::arrow_proxy>, const index<1, SizeType>>; + using Base = std::iterator, std::ptrdiff_t, const details::arrow_proxy>, const index<1>>; public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; + using Base::reference; + using Base::pointer; + using Base::difference_type; + using Base::value_type; using index_type = value_type; - using index_size_type = typename index_type::size_type; + using index_size_type = index_type::size_type; template - explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) noexcept + explicit bounds_iterator(const Bounds&, value_type curr = value_type{}) noexcept : curr( std::move(curr) ) {} reference operator*() const noexcept @@ -1311,9 +1262,9 @@ bounds_iterator operator+(typename bounds_iterator::differ return rhs + n; } -/* -** begin definitions of basic_array_view -*/ +// +// begin definitions of basic_array_view +// namespace details { template @@ -1519,22 +1470,23 @@ private: friend class basic_array_view; }; -template +template struct dim { - static const size_t value = DimSize; + static const std::ptrdiff_t value = DimSize; }; template <> struct dim { - static const size_t value = dynamic_range; - const size_t dvalue; - dim(size_t size) : dvalue(size) {} + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim(std::ptrdiff_t size) : dvalue(size) {} }; -template +template class array_view; -template + +template class strided_array_view; namespace details @@ -1553,31 +1505,31 @@ namespace details using size_type = typename Traits::array_view_traits::size_type; }; - template + template struct ArrayViewArrayTraits { using type = array_view; using value_type = T; - using bounds_type = static_bounds; + using bounds_type = static_bounds; using pointer = T*; using reference = T&; }; - template - struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + template + struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; template - BoundsType newBoundsHelperImpl(size_t totalSize, std::true_type) // dynamic size + BoundsType newBoundsHelperImpl(std::ptrdiff_t , std::true_type) // dynamic size { - fail_fast_assert(totalSize <= details::SizeTypeTraits::max_value); - return BoundsType{static_cast(totalSize)}; + fail_fast_assert(totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; } template - BoundsType newBoundsHelperImpl(size_t totalSize, std::false_type) // static size + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size { fail_fast_assert(BoundsType::static_size == totalSize); return {}; } template - BoundsType newBoundsHelper(size_t totalSize) + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) { static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); return newBoundsHelperImpl(totalSize, std::integral_constant()); @@ -1601,46 +1553,38 @@ namespace details return static_as_array_view_helper(args..., val.dvalue); } - template + template struct static_as_array_view_static_bounds_helper { - using type = static_bounds; + using type = static_bounds<(Dimensions::value)...>; }; template struct is_array_view_oracle : std::false_type {}; - template + + template struct is_array_view_oracle> : std::true_type {}; - template + + template struct is_array_view_oracle> : std::true_type {}; - template + + template struct is_array_view : is_array_view_oracle> {}; } -template -struct array_view_options -{ - struct array_view_traits - { - using value_type = ValueType; - using size_type = SizeType; - }; -}; - -template -class array_view : public basic_array_view::value_type, - static_bounds::size_type, FirstDimension, RestDimensions...>> +template +class array_view : public basic_array_view > { - template + template friend class array_view; - using Base = basic_array_view::value_type, - static_bounds::size_type, FirstDimension, RestDimensions...>>; + + using Base = basic_array_view>; public: using typename Base::bounds_type; @@ -1655,13 +1599,14 @@ public: public: // basic - constexpr array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) - { - } + constexpr array_view(pointer ptr, size_type size) : Base(ptr, bounds_type{ size }) + {} + + constexpr array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + {} constexpr array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) - { - } + {} constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) { @@ -1671,108 +1616,102 @@ public: // default template > constexpr array_view() : Base(nullptr, bounds_type()) - { - } + {} // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template , - typename Dummy = std::enable_if_t::value - && std::is_convertible::value>> - constexpr array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) - { - } + template + typename Dummy = std::enable_if_t::value>> + constexpr array_view(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) + {} // from n-dimensions static array - template , - typename Dummy = std::enable_if_t::value - && std::is_convertible::value>> + template + typename Dummy = std::enable_if_t::value> + > constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) - { - } + {} // from n-dimensions static array with size - template , - typename Dummy = std::enable_if_t::value - && std::is_convertible::value >> + template ::value> + > constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) { fail_fast_assert(size <= N); } // from std array - template , typename Base::bounds_type>::value>> - constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) - { - } - - template , typename Base::bounds_type>::value && std::is_const::value>> - constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) - { - } - + template , typename Base::bounds_type>::value> + > + constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} + template , typename Base::bounds_type>::value + && std::is_const::value> + > + constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} + // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity template ::value - && details::LessThan::value>> // remove literal 0 case + && details::LessThan::value> + > // remove literal 0 case constexpr array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) - { - } + {} // from containers. It must has .size() and .data() two function signatures - template ::value && std::is_convertible::value - && std::is_convertible, typename Base::bounds_type>::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > constexpr array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) - { - - } + {} - constexpr array_view(const array_view &) = default; + constexpr array_view(const array_view &) = default; // convertible - template ::value_type, static_bounds::size_type, FirstDimension, RestDimensions...>>, - typename OtherBaseType = basic_array_view::value_type, static_bounds::size_type, OtherDimensions...>>, + template >, + typename OtherBaseType = basic_array_view>, typename Dummy = std::enable_if_t::value> > - constexpr array_view(const array_view &av) : Base(static_cast::Base &>(av)) {} // static_cast is required + constexpr array_view(const array_view &av) + : Base(static_cast::Base&>(av)) + {} // reshape template - constexpr array_view as_array_view(Dimensions2... dims) + constexpr array_view as_array_view(Dimensions2... dims) { static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); - using BoundsType = typename array_view::bounds_type; + using BoundsType = typename array_view::bounds_type; auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); details::verifyBoundsReshape(this->bounds(), tobounds); return {this->data(), tobounds}; } // to bytes array - template ::value_type>>::value> - constexpr auto as_bytes() const noexcept -> - array_view, static_cast(details::StaticSizeHelper::value)> + template >::value> + constexpr auto as_bytes() const noexcept -> array_view { static_assert(Enabled, "The value_type of array_view must be standarded layout"); return { reinterpret_cast(this->data()), this->bytes() }; } - template ::value_type>>::value> - constexpr auto as_writeable_bytes() const noexcept -> - array_view, static_cast(details::StaticSizeHelper::value)> + template >::value> + constexpr auto as_writeable_bytes() const noexcept -> array_view { static_assert(Enabled, "The value_type of array_view must be standarded layout"); return { reinterpret_cast(this->data()), this->bytes() }; } - - // from bytes array + // from bytes array template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(dynamic_range))> + constexpr auto as_array_view() const noexcept -> array_view { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1781,7 +1720,7 @@ public: } template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(dynamic_range))> + constexpr auto as_array_view() const noexcept -> array_view { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1790,43 +1729,43 @@ public: } // section on linear space - template - constexpr array_view first() const noexcept + template + constexpr array_view first() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed return { this->data(), Count }; } - constexpr array_view first(size_type count) const noexcept + constexpr array_view first(size_type count) const noexcept { fail_fast_assert(count <= this->size()); return { this->data(), count }; } - template - constexpr array_view last() const noexcept + template + constexpr array_view last() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data() + this->size() - Count, Count }; } - constexpr array_view last(size_type count) const noexcept + constexpr array_view last(size_type count) const noexcept { fail_fast_assert(count <= this->size()); return { this->data() + this->size() - count, count }; } - template - constexpr array_view sub() const noexcept + template + constexpr array_view sub() const noexcept { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } - constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept + constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept { fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; @@ -1837,24 +1776,27 @@ public: { return this->size(); } + constexpr size_type used_length() const noexcept { return length(); } + constexpr size_type bytes() const noexcept { return sizeof(value_type) * this->size(); } + constexpr size_type used_bytes() const noexcept { return bytes(); } // section - constexpr strided_array_view section(index_type origin, index_type extents) const + constexpr strided_array_view section(index_type origin, index_type extents) const { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; } constexpr reference operator[](const index_type& idx) const @@ -1863,7 +1805,7 @@ public: } template 1), typename Dummy = std::enable_if_t> - constexpr array_view operator[](size_type idx) const + constexpr array_view operator[](size_type idx) const { auto ret = Base::operator[](idx); return{ ret.data(), ret.bounds() }; @@ -1877,20 +1819,20 @@ public: using Base::operator>=; }; -template -constexpr auto as_array_view(T * const & ptr, dim... args) -> array_view, Dimensions...> +template +constexpr auto as_array_view(T* const& ptr, dim... args) -> array_view, Dimensions...> { - return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; + return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; } template -constexpr auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_array_view (T* arr, std::ptrdiff_t len) -> typename details::ArrayViewArrayTraits::type { - return {arr, len}; + return {reinterpret_cast*>(arr), len}; } template -constexpr auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type { return {arr}; } @@ -1927,13 +1869,14 @@ template constexpr auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, array_view, dynamic_range>> = delete; -template -class strided_array_view : public basic_array_view::value_type, strided_bounds::size_type>> +template +class strided_array_view : public basic_array_view> { - using Base = basic_array_view::value_type, strided_bounds::size_type>>; + using Base = basic_array_view>; - template + template friend class strided_array_view; + public: using Base::rank; using typename Base::bounds_type; @@ -1959,21 +1902,20 @@ public: } // from array view - template > - strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) + template > + strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) { fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); } // convertible - template ::value_type, strided_bounds::size_type>>, - typename OtherBaseType = basic_array_view::value_type, strided_bounds::size_type>>, + template >, + typename OtherBaseType = basic_array_view>, typename Dummy = std::enable_if_t::value> > - constexpr strided_array_view(const strided_array_view &av): Base(static_cast::Base &>(av)) // static_cast is required - { - } + constexpr strided_array_view(const strided_array_view &av) : Base(static_cast::Base &>(av)) // static_cast is required + {} // convert from bytes template @@ -2047,17 +1989,21 @@ public: using typename Base::reference; using typename Base::pointer; using typename Base::difference_type; + private: template friend class basic_array_view; - pointer m_pdata; + + pointer m_pdata; const ArrayView * m_validator; void validateThis() const { fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } - contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : + + contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } + public: reference operator*() const noexcept { @@ -2170,12 +2116,14 @@ public: private: template friend class basic_array_view; - const ArrayView * m_container; + + const ArrayView * m_container; typename ArrayView::bounds_type::iterator m_itr; - general_array_view_iterator(const ArrayView *container, bool isbegin = false) : - m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } + + general_array_view_iterator(const ArrayView *container, bool isbegin = false) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + {} + public: reference operator*() const noexcept { diff --git a/include/string_view.h b/include/string_view.h index 4076d52..04909f8 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -34,16 +34,16 @@ namespace gsl // type system for these types that will not either incur significant runtime costs or // (sometimes needlessly) break existing programs when introduced. // -template +template using czstring = const char*; -template +template using cwzstring = const wchar_t*; -template +template using zstring = char*; -template +template using wzstring = wchar_t*; // @@ -54,19 +54,19 @@ using wzstring = wchar_t*; // // TODO (neilmac) once array_view regains configurable size_type, update these typedef's // -template +template using basic_string_view = array_view; -template +template using string_view = basic_string_view; -template +template using cstring_view = basic_string_view; -template +template using wstring_view = basic_string_view; -template +template using cwstring_view = basic_string_view; @@ -94,38 +94,39 @@ array_view ensure_sentinel(const T* seq, SizeType max = std::n // the limit of size_type. // template -inline basic_string_view ensure_z(T* const & sz, size_t max = std::numeric_limits::max()) +inline basic_string_view ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel<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 ensure_z(char* const & sz, size_t max) +inline basic_string_view ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, len }; + fail_fast_assert(sz[len] == 0); + return{ sz, static_cast(len) }; } -inline basic_string_view ensure_z(const char* const& sz, size_t max) +inline basic_string_view ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, len }; + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; } -inline basic_string_view ensure_z(wchar_t* const & sz, size_t max) +inline basic_string_view ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, len }; + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; } -inline basic_string_view ensure_z(const wchar_t* const & sz, size_t max) +inline basic_string_view ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, len }; + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; } template -basic_string_view ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], N); } +basic_string_view ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template basic_string_view::type, dynamic_range> ensure_z(Cont& cont) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 918df9e..5b06e19 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -50,7 +50,7 @@ SUITE(array_view_tests) delete[] ptr.data(); - static_bounds bounds{ 3 }; + static_bounds<4, dynamic_range, 2> bounds{ 3 }; #ifdef CONFIRM_COMPILATION_ERRORS array_view av(nullptr, bounds); @@ -151,9 +151,9 @@ SUITE(array_view_tests) TEST(md_access) { - unsigned int width = 5, height = 20; + auto width = 5, height = 20; - unsigned int imgSize = width * height; + auto imgSize = width * height; auto image_ptr = new int[imgSize][3]; // size check will be done @@ -162,9 +162,9 @@ SUITE(array_view_tests) iota(image_view.begin(), image_view.end(), 1); int expected = 0; - for (unsigned int i = 0; i < height; i++) + for (auto i = 0; i < height; i++) { - for (unsigned int j = 0; j < width; j++) + for (auto j = 0; j < width; j++) { CHECK(expected + 1 == image_view[i][j][0]); CHECK(expected + 2 == image_view[i][j][1]); @@ -244,7 +244,7 @@ SUITE(array_view_tests) auto av8 = av7.as_array_view(); CHECK(av8.size() == av6.size()); - for (size_t i = 0; i < av8.size(); i++) + for (auto i = 0; i < av8.size(); i++) { CHECK(av8[i] == 1); } diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index c3f549f..aacf3d8 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -23,16 +23,16 @@ using namespace gsl;; namespace { - void use(unsigned int&) {} + void use(std::ptrdiff_t&) {} } SUITE(bounds_test) { TEST(basic_bounds) { - for (auto point : static_bounds { 2 }) + for (auto point : static_bounds { 2 }) { - for (unsigned int j = 0; j < decltype(point)::rank; j++) + for (decltype(point)::size_type j = 0; j < decltype(point)::rank; j++) { use(j); use(point[j]); @@ -42,24 +42,24 @@ SUITE(bounds_test) TEST(bounds_basic) { - static_bounds b; + static_bounds<3, 4, 5> b; auto a = b.slice(); - static_bounds x{ 4 }; + static_bounds<4, dynamic_range, 2> x{ 4 }; x.slice().slice(); } TEST (arrayview_iterator) { - static_bounds bounds{ 3 }; + static_bounds<4, dynamic_range, 2> bounds{ 3 }; auto itr = bounds.begin(); #ifdef CONFIRM_COMPILATION_ERRORS - array_view< int, 4, dynamic_range, 2> av(nullptr, bounds); + array_view 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 +68,24 @@ SUITE(bounds_test) TEST (bounds_convertible) { - static_bounds b1; - static_bounds b2 = b1; + static_bounds<7, 4, 2> b1; + static_bounds<7, dynamic_range, 2> b2 = b1; #ifdef CONFIRM_COMPILATION_ERRORS - static_bounds b4 = b2; + static_bounds<7, dynamic_range, 1> b4 = b2; #endif - static_bounds b3 = b1; - static_bounds b4 = b3; + static_bounds b3 = b1; + static_bounds<7, 4, 2> b4 = b3; - static_bounds b11; + static_bounds b11; - static_bounds b5; - static_bounds b6; + static_bounds b5; + static_bounds<34> b6; - b5 = static_bounds(); + b5 = static_bounds<20>(); CHECK_THROW(b6 = b5, fail_fast); - b5 = static_bounds(); + b5 = static_bounds<34>(); b6 = b5; CHECK(b5 == b6); -- cgit v1.2.3 From 01868f2516ee04e2129ceafe341fd5fe0d112e01 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 15 Oct 2015 16:48:38 -0700 Subject: Fix missing header for std::divides. --- include/array_view.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/array_view.h b/include/array_view.h index c884c11..cc7ab23 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "fail_fast.h" #ifdef _MSC_VER -- cgit v1.2.3 From c973e82dff09144005e883a3daceded35044472f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 15 Oct 2015 17:05:19 -0700 Subject: Added AppVeyor CI status badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d96dec..c687673 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) +# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). -- cgit v1.2.3 From a4654a46b535100f53143b6c616425cf293b6a3b Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 16 Oct 2015 12:15:22 -0700 Subject: Removed arrow_proxy class, fixes bugs in reverse bounds_iterator --- include/array_view.h | 265 ++++++++++++++++++++++++--------------------- tests/array_view_tests.cpp | 63 ++++++++++- 2 files changed, 197 insertions(+), 131 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index cc7ab23..a31efd8 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -74,25 +74,6 @@ namespace details { static const SizeType max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); }; - - template - class arrow_proxy - { - public: - explicit arrow_proxy(T t) - : val(t) - {} - const T operator*() const noexcept - { - return val; - } - const T* operator->() const noexcept - { - return &val; - } - private: - T val; - }; } template @@ -730,8 +711,9 @@ public: using size_type = SizeType; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; using difference_type = ptrdiff_t; using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; @@ -822,7 +804,7 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this); + return const_iterator(*this, index_type{}); } constexpr const_iterator end() const noexcept @@ -845,8 +827,9 @@ public: using difference_type = SizeType; using value_type = SizeType; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; static const int dynamic_rank = rank; static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; @@ -920,11 +903,11 @@ public: { return m_extents; } - const_iterator begin() const noexcept + constexpr const_iterator begin() const noexcept { - return const_iterator{ *this }; + return const_iterator{ *this, index_type{} }; } - const_iterator end() const noexcept + constexpr const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } @@ -941,15 +924,11 @@ template struct is_bounds> : std::integral_constant {}; template -class bounds_iterator - : public std::iterator, - const IndexType> +class bounds_iterator: public std::iterator { private: - using Base = std::iterator , const IndexType>; + using Base = std::iterator ; + public: static const size_t rank = IndexType::rank; using typename Base::reference; @@ -959,79 +938,88 @@ public: using index_type = value_type; using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds& bnd, value_type curr = value_type{}) noexcept - : boundary(bnd.index_bounds()) - , curr(std::move(curr)) + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary(bnd.index_bounds()), curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - reference operator*() const noexcept + + constexpr reference operator*() const noexcept { return curr; } - pointer operator->() const noexcept + + constexpr pointer operator->() const noexcept { - return details::arrow_proxy{ curr }; + return &curr; } - bounds_iterator& operator++() noexcept + + constexpr bounds_iterator& operator++() noexcept { for (size_t i = rank; i-- > 0;) { - if (++curr[i] < boundary[i]) + if (curr[i] < boundary[i] - 1) { + curr[i]++; return *this; } - else - { - curr[i] = 0; - } + curr[i] = 0; } // If we're here we've wrapped over - set to past-the-end. - for (size_t i = 0; i < rank; ++i) - { - curr[i] = boundary[i]; - } + curr = boundary; return *this; } - bounds_iterator operator++(int) noexcept + + constexpr bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() noexcept + + constexpr bounds_iterator& operator--() noexcept { - for (size_t i = rank; i-- > 0;) + if (!less(curr, boundary)) { - if (curr[i]-- > 0) + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { - return *this; + curr[i] = boundary[i] - 1; } - else + return *this; + } + for (size_t i = rank; i-- > 0;) + { + if (curr[i] >= 1) { - curr[i] = boundary[i] - 1; + curr[i]--; + return *this; } + curr[i] = boundary[i] - 1; } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" fail_fast_assert(false); return *this; } - bounds_iterator operator--(int) noexcept + + constexpr bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const noexcept + + constexpr bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) noexcept + + constexpr bounds_iterator& operator+=(difference_type n) noexcept { auto linear_idx = linearize(curr) + n; - value_type stride; + std::remove_const_t stride; stride[rank - 1] = 1; for (size_t i = rank - 1; i-- > 0;) { @@ -1042,76 +1030,84 @@ public: curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } + fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); return *this; } - bounds_iterator operator-(difference_type n) const noexcept + + constexpr bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) noexcept + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const noexcept + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr) - linearize(rhs.curr); } - reference operator[](difference_type n) const noexcept + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const bounds_iterator& rhs) const noexcept + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept { - for (size_t i = 0; i < rank; ++i) - { - if (curr[i] < rhs.curr[i]) - return true; - } - return false; + return less(curr, rhs.curr); } - bool operator<=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(bounds_iterator& rhs) noexcept { std::swap(boundary, rhs.boundary); std::swap(curr, rhs.curr); } private: - index_size_type linearize(const value_type& idx) const noexcept + constexpr bool less(index_type& one, index_type& other) const noexcept { - // TODO: Smarter impl. - // Check if past-the-end - bool pte = true; for (size_t i = 0; i < rank; ++i) { - if (idx[i] != boundary[i]) - { - pte = false; - break; - } + if (one[i] < other[i]) + return true; } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end index_size_type multiplier = 1; index_size_type res = 0; - if (pte) + if (!less(idx, boundary)) { res = 1; for (size_t i = rank; i-- > 0;) @@ -1130,19 +1126,15 @@ private: } return res; } + value_type boundary; - value_type curr; + std::remove_const_t curr; }; template -class bounds_iterator> - : public std::iterator, - ptrdiff_t, - const details::arrow_proxy>, - const index<1, SizeType>> +class bounds_iterator> : public std::iterator> { - using Base = std::iterator, ptrdiff_t, const details::arrow_proxy>, const index<1, SizeType>>; + using Base = std::iterator>; public: using typename Base::reference; @@ -1153,96 +1145,116 @@ public: using index_size_type = typename index_type::value_type; template - explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) noexcept - : curr( std::move(curr) ) + constexpr explicit bounds_iterator(const Bounds&, value_type curr) noexcept + : curr(std::move(curr)) {} - reference operator*() const noexcept + + constexpr reference operator*() const noexcept { return curr; } - pointer operator->() const noexcept + + constexpr pointer operator->() const noexcept { - return details::arrow_proxy{ curr }; + &curr; } - bounds_iterator& operator++() noexcept + + constexpr bounds_iterator& operator++() noexcept { ++curr; return *this; } - bounds_iterator operator++(int) noexcept + + constexpr bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() noexcept + + constexpr bounds_iterator& operator--() noexcept { curr--; return *this; } - bounds_iterator operator--(int) noexcept + + constexpr bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const noexcept + + constexpr bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) noexcept + + constexpr bounds_iterator& operator+=(difference_type n) noexcept { curr += n; return *this; } - bounds_iterator operator-(difference_type n) const noexcept + + constexpr bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) noexcept + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const noexcept + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return curr[0] - rhs.curr[0]; } - reference operator[](difference_type n) const noexcept + + constexpr reference operator[](difference_type n) const noexcept { return curr + n; } - bool operator==(const bounds_iterator& rhs) const noexcept + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept { return curr[0] < rhs.curr[0]; } - bool operator<=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(bounds_iterator& rhs) noexcept + + constexpr void swap(bounds_iterator& rhs) noexcept { std::swap(curr, rhs.curr); } + private: - value_type curr; + std::remove_const_t curr; }; template @@ -1304,10 +1316,11 @@ public: using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; using value_type = ValueType; + using const_value_type = std::add_const_t; using pointer = ValueType*; using reference = ValueType&; using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; - using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using sliced_type = std::conditional_t>; @@ -1360,7 +1373,7 @@ public: } constexpr iterator end() const { - return iterator {this}; + return iterator {this, false}; } constexpr const_iterator cbegin() const { @@ -1368,7 +1381,7 @@ public: } constexpr const_iterator cend() const { - return const_iterator {reinterpret_cast *>(this)}; + return const_iterator {reinterpret_cast *>(this), false}; } constexpr reverse_iterator rbegin() const @@ -1999,8 +2012,8 @@ private: { fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } - contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } + contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} public: reference operator*() const noexcept { @@ -2115,16 +2128,16 @@ private: friend class basic_array_view; const ArrayView * m_container; typename ArrayView::bounds_type::iterator m_itr; - general_array_view_iterator(const ArrayView *container, bool isbegin = false) : + general_array_view_iterator(const ArrayView *container, bool isbegin) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) { } public: - reference operator*() const noexcept + reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() const noexcept + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index f84e908..cf83fd5 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -925,11 +925,35 @@ SUITE(array_view_tests) } } - size_t idx = 0; - for (auto num : section) + size_t check_sum = 0; + for (size_t i = 0; i < length; ++i) { - CHECK(num == av[idx][1]); - idx++; + check_sum += av[i][1]; + } + + { + size_t idx = 0; + size_t sum = 0; + for (auto num : section) + { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) + { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); } } @@ -1688,7 +1712,7 @@ SUITE(array_view_tests) CHECK_THROW(f(), fail_fast); } - TEST(AsWriteableBytes) + TEST(AsWriteableBytes) { int a[] = { 1, 2, 3, 4 }; @@ -1714,7 +1738,36 @@ SUITE(array_view_tests) CHECK(wav.data() == (byte*)&a[0]); CHECK(wav.length() == sizeof(a)); } + } + + TEST(NonConstIterator) + { + int a[] = { 1, 2, 3, 4 }; + { + array_view av = a; + auto wav = av.as_writeable_bytes(); + for (auto& b : wav) + { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 0); + } + } + + { + array_view av = a; + for (auto& n : av) + { + n = 1; + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 1); + } + } } TEST(ArrayViewComparison) -- cgit v1.2.3 From a544ada8fe998bc7c47be942008430a7ec8366da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Garc=C3=ADa=20Salas?= Date: Sat, 17 Oct 2015 08:53:58 +0200 Subject: std::hash support for gsl::not_null. --- include/gsl.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/gsl.h b/include/gsl.h index 519682b..ca37848 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -197,6 +197,19 @@ private: } // namespace gsl +namespace std +{ + template + struct hash> + { + size_t operator()(const gsl::not_null & value) const + { + return hash{}(value); + } + }; + +} // namespace std + #ifdef _MSC_VER #undef constexpr -- cgit v1.2.3 From 8e2acc9c901c658cfcce013ecaf594b9565b2fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Garc=C3=ADa=20Salas?= Date: Sat, 17 Oct 2015 09:28:05 +0200 Subject: std::hash support for gsl::not_null. --- include/gsl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index ca37848..ec75723 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -200,11 +200,11 @@ private: namespace std { template - struct hash> + struct hash> { - size_t operator()(const gsl::not_null & value) const + size_t operator()(const gsl::not_null & value) const { - return hash{}(value); + return hash{}(value); } }; -- cgit v1.2.3 From 5f26ddac70f05766070b5b62ecabb7086f0df7c2 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 16 Oct 2015 17:30:48 -0700 Subject: Replaced index constructor from initializer list by a constructor from static list Conflicts: include/array_view.h --- include/array_view.h | 11 ++--- tests/array_view_tests.cpp | 110 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 96 insertions(+), 25 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index a31efd8..a2ea49f 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -72,7 +72,7 @@ namespace details template struct SizeTypeTraits { - static const SizeType max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + static const SizeType max_value = std::numeric_limits::max(); }; } @@ -99,12 +99,9 @@ public: std::copy(values, values + Rank, elems); } - // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) noexcept - { - fail_fast_assert(il.size() == Rank, "The size of the initializer list must match the rank of the array"); - std::copy(begin(il), end(il), elems); - } + template> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} constexpr index(const index& other) noexcept = default; diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index cf83fd5..3a8acc2 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -16,16 +16,11 @@ #include #include -#include -#include -#include + #include #include #include #include -#include -#include - using namespace std; using namespace gsl; @@ -639,23 +634,14 @@ SUITE(array_view_tests) index<1> index{ 0, 1 }; strided_array_view sav8{ arr,{ 1,{ 1,1 } } }; -#ifdef _MSC_VER strided_array_view sav9{ arr,{ { 1,1 },{ 1,1 } } }; -#endif strided_array_view sav10{ av,{ 1,{ 1,1 } } }; -#ifdef _MSC_VER strided_array_view sav11{ av,{ { 1,1 },{ 1,1 } } }; -#endif + strided_array_view sav12{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_array_view sav13{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_array_view sav14{ av.as_array_view(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; } #endif - - { - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); -#endif - } } TEST(strided_array_view_type_conversion) @@ -839,6 +825,94 @@ SUITE(array_view_tests) delete[] arr; } + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = { 0,1,2 }; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // from other index of bigger size type + index<3, short> i5 = i4; + CHECK(i5[0] == 0); + + // from other index of smaller size type + index<3, long long> i6 = i4; + CHECK(i6[0] == 0); + + // default + index<3, long long> i7; + CHECK(i7[0] == 0); + + // default + index<3, long long> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = { 0 }; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // from other index of bigger size type + index<1, short> i6 = i5; + CHECK(i6[0] == 0); + + // from other index of smaller size type + index<1, long long> i7 = i6; + CHECK(i7[0] == 0); + + // default + index<1, long long> i8; + CHECK(i8[0] == 0); + + // default + index<1, long long> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = { 0 }; + index<3> i4 = { 0, 1, 2, 3 }; + index<1> i5 = { 0,1 }; + } +#endif + } + TEST(index_operations) { size_t a[3] = { 0, 1, 2 }; -- cgit v1.2.3 From 1c208b33d0823dabed0832b91ce6e56fbcd046cd Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 16 Oct 2015 17:40:57 -0700 Subject: Removed specializations for Rank=1 --- include/array_view.h | 271 ++------------------------------------------------- 1 file changed, 8 insertions(+), 263 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index a2ea49f..0145799 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -74,6 +74,13 @@ namespace details { static const SizeType max_value = std::numeric_limits::max(); }; + + + template + class are_integral : public std::integral_constant {}; + + template + class are_integral : public std::integral_constant::value && are_integral::value> {}; } template @@ -99,7 +106,7 @@ public: std::copy(values, values + Rank, elems); } - template> + template::value, typename Dummy = std::enable_if_t> constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } {} @@ -227,142 +234,6 @@ private: value_type elems[Rank] = {}; }; -template -class index<1, ValueType> -{ - template - friend class index; - -public: - static const size_t rank = 1; - using value_type = std::remove_reference_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr index() noexcept : value(0) - {} - - constexpr index(value_type e) noexcept : value(e) - {} - - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - {} - - constexpr index(const index &) noexcept = default; - - template ::max_value <= details::SizeTypeTraits::max_value), - typename Other = std::enable_if_t>> - constexpr index(const index<1, OtherValueType>& other) noexcept - { - value = static_cast(other.value); - } - - template ::max_value > details::SizeTypeTraits::max_value), - typename Other = std::enable_if_t>> - constexpr index(const index<1, OtherValueType>& other, void* ptr=0) noexcept - { - fail_fast_assert(other.value <= static_cast(details::SizeTypeTraits::max_value)); - value = static_cast(other.value); - } - - // Preconditions: component_idx < 1 - constexpr reference operator[](value_type component_idx) noexcept - { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); - return value; - } - // Preconditions: component_idx < 1 - constexpr const_reference operator[](value_type component_idx) const noexcept - { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); - return value; - } - constexpr bool operator==(const index& rhs) const noexcept - { - return value == rhs.value; - } - constexpr bool operator!=(const index& rhs) const noexcept - { - return !(*this == rhs); - } - constexpr index operator+() const noexcept - { - return *this; - } - constexpr index operator-() const noexcept - { - return index(-value); - } - constexpr index operator+(const index& rhs) const noexcept - { - return index(value + rhs.value); - } - constexpr index operator-(const index& rhs) const noexcept - { - return index(value - rhs.value); - } - constexpr index& operator+=(const index& rhs) noexcept - { - value += rhs.value; - return *this; - } - constexpr index& operator-=(const index& rhs) noexcept - { - value -= rhs.value; - return *this; - } - constexpr index& operator++() noexcept - { - ++value; - return *this; - } - constexpr index operator++(int) noexcept - { - index ret = *this; - ++(*this); - return ret; - } - constexpr index& operator--() noexcept - { - --value; - return *this; - } - constexpr index operator--(int) noexcept - { - index ret = *this; - --(*this); - return ret; - } - constexpr index operator*(value_type v) const noexcept - { - return index(value * v); - } - constexpr index operator/(value_type v) const noexcept - { - return index(value / v); - } - constexpr index& operator*=(value_type v) noexcept - { - value *= v; - return *this; - } - constexpr index& operator/=(value_type v) noexcept - { - value /= v; - return *this; - } - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return{ rhs * v }; - } -private: - value_type value; -}; - #ifndef _MSC_VER struct static_bounds_dynamic_range_t @@ -1128,132 +999,6 @@ private: std::remove_const_t curr; }; -template -class bounds_iterator> : public std::iterator> -{ - using Base = std::iterator>; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename index_type::value_type; - - template - constexpr explicit bounds_iterator(const Bounds&, value_type curr) noexcept - : curr(std::move(curr)) - {} - - constexpr reference operator*() const noexcept - { - return curr; - } - - constexpr pointer operator->() const noexcept - { - &curr; - } - - constexpr bounds_iterator& operator++() noexcept - { - ++curr; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - curr--; - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - curr += n; - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return curr[0] - rhs.curr[0]; - } - - constexpr reference operator[](difference_type n) const noexcept - { - return curr + n; - } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr == rhs.curr; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return curr[0] < rhs.curr[0]; - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - constexpr void swap(bounds_iterator& rhs) noexcept - { - std::swap(curr, rhs.curr); - } - -private: - std::remove_const_t curr; -}; - template bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept { -- cgit v1.2.3 From 59cf62652a4bb6ee5809727f86ed7a5b218c3821 Mon Sep 17 00:00:00 2001 From: Matt Newport Date: Mon, 19 Oct 2015 18:55:46 -0700 Subject: Add .gitignore --- .gitignore | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5f5de3e..3fb78a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,13 @@ -tests/unittest-cpp \ No newline at end of file +tests/unittest-cpp +CMakeFiles +tests/CMakeFiles +tests/Debug +*.opensdf +*.sdf +tests/*tests.dir +*.vcxproj +*.vcxproj.filters +*.sln +*.tlog +Testing/Temporary/*.* +CMakeCache.txt -- cgit v1.2.3 From 9e9eddcddfef2fd6f80002f476cfb9f46b511c31 Mon Sep 17 00:00:00 2001 From: Matt Newport Date: Mon, 19 Oct 2015 18:58:26 -0700 Subject: Updated .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3fb78a5..ea47eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ tests/*tests.dir *.tlog Testing/Temporary/*.* CMakeCache.txt +*.suo -- cgit v1.2.3 From 561da1cd0049b24bc2cb26a77e7ec4df43ed1f37 Mon Sep 17 00:00:00 2001 From: Kosov Eugene Date: Wed, 21 Oct 2015 13:31:00 +0300 Subject: fix clang warning on unused function parameter --- include/array_view.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/array_view.h b/include/array_view.h index 0145799..14d8888 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -125,7 +125,7 @@ public: template ::max_value > details::SizeTypeTraits::max_value), typename Other = std::enable_if_t>> - constexpr index(const index& other, void* ptr = 0) noexcept + constexpr index(const index& other, void* = 0) noexcept { bool ok = std::accumulate(other.elems, other.elems + Rank, true, [&](bool b, OtherValueType val) { return b && (val <= static_cast(details::SizeTypeTraits::max_value)); } -- cgit v1.2.3 From ace9ab9d3b7f01b322bad89c7373e64d8782b07e Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 23 Oct 2015 19:49:17 -0700 Subject: Building again. Some tests failing. --- include/array_view.h | 141 +++++++++---------------- tests/array_view_tests.cpp | 253 ++++++++++----------------------------------- 2 files changed, 102 insertions(+), 292 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 4211637..4d5d46a 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -98,7 +98,8 @@ class index final public: static const size_t rank = Rank; - using value_type = std::remove_reference_t; + using value_type = std::ptrdiff_t; + using size_type = value_type; using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; @@ -118,30 +119,6 @@ public: } constexpr index(const index& other) noexcept = default; - - // copy from index over smaller domain - template ::max_value <= details::SizeTypeTraits::max_value), - typename Other = std::enable_if_t>> - constexpr index(const index& other) noexcept - { - std::copy(other.elems, other.elems + Rank, elems); - } - - // copy from index over larger domain - template ::max_value > details::SizeTypeTraits::max_value), - typename Other = std::enable_if_t>> - constexpr index(const index& other, void* ptr = 0) noexcept - { - bool ok = std::accumulate(other.elems, other.elems + Rank, true, - [&](bool b, OtherValueType val) { return b && (val <= static_cast(details::SizeTypeTraits::max_value)); } - ); - - fail_fast_assert(ok, "other value must fit in the new domain"); - std::transform(other.elems, other.elems + rank, elems, [&](OtherValueType val) { return static_cast(val); }); - } - constexpr index& operator=(const index& rhs) noexcept = default; // Preconditions: component_idx < rank @@ -196,13 +173,13 @@ public: constexpr index& operator+=(const index& rhs) noexcept { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); return *this; } constexpr index& operator-=(const index& rhs) noexcept { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); return *this; } @@ -227,13 +204,13 @@ public: constexpr index& operator*=(value_type v) noexcept { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); return *this; } constexpr index& operator/=(value_type v) noexcept { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); return *this; } @@ -250,29 +227,13 @@ public: using value_type = std::ptrdiff_t; using reference = std::add_lvalue_reference_t; using const_reference = const std::ptrdiff_t&;//std::add_const_t>; - - constexpr index() noexcept : value(0) - {} - constexpr index(value_type e0) noexcept : value(e0) - {} + template + friend class index; - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) + constexpr index() noexcept : value(0) {} - // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) - { - fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); - value = begin(il)[0]; - } - - template - friend class index; - - constexpr index() noexcept : value(0) - {} - constexpr index(value_type e) noexcept : value(e) {} @@ -416,6 +377,11 @@ namespace details static const size_type CurrentRange = 1; static const size_type TotalSize = 1; + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + {} + BoundsRanges (const BoundsRanges&) = default; BoundsRanges(const size_type* const) { } BoundsRanges() = default; @@ -529,8 +495,7 @@ namespace details static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum; static const size_type CurrentRange = CurRange; - static const size_type TotalSize = CurrentRange; - static_assert (CurRange <= PTRDIFF_MAX, "CurRange must be smaller than std::ptrdiff_t limits"); + static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; BoundsRanges (const BoundsRanges&) = default; BoundsRanges(const size_type* const arr) : Base(arr) { } @@ -645,8 +610,8 @@ namespace details return TypeListIndexer(obj); } - template 1), typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept { Ret ret; for (size_t i = 0; i < Rank - 1; ++i) @@ -787,40 +752,30 @@ public: }; template -class strided_bounds : private details::coordinate_facade, Rank> +class strided_bounds { - using Base = details::coordinate_facade, Rank>; - friend Base; - template friend class strided_bounds; public: static const size_t rank = Rank; - using reference = SizeType&; - using const_reference = const SizeType&; - using size_type = SizeType; - using difference_type = SizeType; - using value_type = SizeType; - using index_type = index; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; using iterator = bounds_iterator; using const_iterator = bounds_iterator; static const size_t dynamic_rank = rank; - static const std::ptrdiff_t static_size = dynamic_range; + static const value_type static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; constexpr strided_bounds(const strided_bounds &) noexcept = default; - constexpr strided_bounds(const index_type& extents, const index_type& strides) - : m_strides(strides) - { - for (size_t i = 0; i < rank; i++) - Base::elems[i] = extents[i]; - } - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : Base(values), m_strides(std::move(strides)) + : m_extents(values), m_strides(std::move(strides)) {} constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept @@ -1487,7 +1442,7 @@ namespace details struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; template - BoundsType newBoundsHelperImpl(std::ptrdiff_t , std::true_type) // dynamic size + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size { fail_fast_assert(totalSize <= PTRDIFF_MAX); return BoundsType{totalSize}; @@ -1584,26 +1539,25 @@ public: } // default - template > + template > constexpr array_view() : Base(nullptr, bounds_type()) {} // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) template - typename Dummy = std::enable_if_t::value>> + /*typename Dummy = std::enable_if_t::value>*/> constexpr array_view(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) {} // from n-dimensions static array - template - typename Dummy = std::enable_if_t::value> - > - constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + template , + typename = std::enable_if_t::value>> + constexpr array_view (T (&arr)[N]) : Base(arr, Helper::bounds_type()) {} // from n-dimensions static array with size - template ::value> + template , + typename Dummy = std::enable_if_t::value> > constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) { @@ -1680,22 +1634,22 @@ public: } // from bytes array - template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view + template::value, typename = std::enable_if_t> + constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } template::value, typename Dummy = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view + constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } // section on linear space @@ -1832,7 +1786,8 @@ template constexpr auto as_array_view(Cont &arr) -> std::enable_if_t>::value, array_view, dynamic_range>> { - return {arr.data(), arr.size()}; + fail_fast_assert(arr.size() < PTRDIFF_MAX); + return {arr.data(), static_cast(arr.size())}; } template @@ -1892,7 +1847,7 @@ public: strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const { static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = sizeof(OtherValueType) / sizeof(value_type); + auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); size_type size = this->bounds().total_size() / d; return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; @@ -1917,7 +1872,7 @@ public: } private: - static index_type resize_extent(const index_type& extent, size_t d) + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) { fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); @@ -1928,7 +1883,7 @@ private: } template > - static index_type resize_stride(const index_type& strides, size_t , void * = 0) + static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) { fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); @@ -1936,7 +1891,7 @@ private: } template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, size_t d) + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) { fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 19889c3..1e072d4 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -227,11 +227,12 @@ SUITE(array_view_tests) #endif } } - + template void fn(Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } TEST (array_view_reshape_test) { int a[3][4][5]; auto av = as_array_view(a); + fn(av.bounds()); auto av2 = av.as_array_view(dim<60>()); auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); @@ -244,11 +245,11 @@ SUITE(array_view_tests) auto av8 = av7.as_array_view(); - CHECK(av8.size() == av6.size()); - for (auto i = 0; i < av8.size(); i++) - { - CHECK(av8[i] == 1); - } + //CHECK(av8.size() == av6.size()); + //for (auto i = 0; i < av8.size(); i++) + //{ + // CHECK(av8[i] == 1); + //} #ifdef CONFIRM_COMPILATION_ERRORS struct Foo {char c[11];}; @@ -314,7 +315,7 @@ SUITE(array_view_tests) // From non-cv-qualified source { - const array_view src{ arr }; + const array_view src = arr; strided_array_view sav{ src, {2, 1} }; CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); @@ -824,13 +825,13 @@ SUITE(array_view_tests) array_view av(arr, 8); - size_t a[1] = { 0 }; + ptrdiff_t a[1] = { 0 }; index<1> i = a; CHECK(av[i] == 4); auto av2 = av.as_array_view(dim<4>(), dim<>(2)); - size_t a2[2] = { 0, 1 }; + ptrdiff_t a2[2] = { 0, 1 }; index<2> i2 = a2; CHECK(av2[i2] == 0); @@ -841,8 +842,8 @@ SUITE(array_view_tests) TEST(index_operations) { - size_t a[3] = { 0, 1, 2 }; - size_t b[3] = { 3, 4, 5 }; + ptrdiff_t a[3] = { 0, 1, 2 }; + ptrdiff_t b[3] = { 3, 4, 5 }; index<3> i = a; index<3> j = b; @@ -903,12 +904,12 @@ SUITE(array_view_tests) auto section = av.section({ 0,1 }, { length,1 }); CHECK(section.size() == length); - for (unsigned int i = 0; i < section.size(); ++i) + for (auto i = 0; i < section.size(); ++i) { CHECK(section[i][0] == av[i][1]); } - for (unsigned int i = 0; i < section.size(); ++i) + for (auto i = 0; i < section.size(); ++i) { auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro CHECK(section[idx] == av[i][1]); @@ -916,16 +917,16 @@ SUITE(array_view_tests) CHECK(section.bounds().index_bounds()[0] == length); CHECK(section.bounds().index_bounds()[1] == 1); - for (unsigned int i = 0; i < section.bounds().index_bounds()[0]; ++i) + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { - for (unsigned int j = 0; j < section.bounds().index_bounds()[1]; ++j) + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro CHECK(section[idx] == av[i][1]); } } - size_t idx = 0; + ptrdiff_t idx = 0; for (auto num : section) { CHECK(num == av[idx][1]); @@ -961,11 +962,11 @@ SUITE(array_view_tests) TEST(dynamic_array_view_section_iteration) { - unsigned int height = 4, width = 2; - unsigned int size = height * width; + auto height = 4, width = 2; + auto size = height * width; auto arr = new int[size]; - for (int unsigned i = 0; i < size; ++i) + for (auto i = 0; i < size; ++i) { arr[i] = i; } @@ -1005,7 +1006,7 @@ SUITE(array_view_tests) CHECK(strided.size() == length); CHECK(strided.bounds().index_bounds()[0] == length); - for (unsigned int i = 0; i < strided.size(); ++i) + for (auto i = 0; i < strided.size(); ++i) { CHECK(strided[i] == av[2 * i + 1]); } @@ -1055,20 +1056,20 @@ SUITE(array_view_tests) int expected[6] = { 2,3,10,11,18,19 }; auto section = av.section({ 0,1,0 }, { 3,1,2 }); - for (unsigned int i = 0; i < section.extent<0>(); ++i) + for (auto i = 0; i < section.extent<0>(); ++i) { - for (unsigned int j = 0; j < section.extent<1>(); ++j) - for (unsigned int k = 0; k < section.extent<2>(); ++k) + 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 (unsigned int i = 0; i < section.extent<0>(); ++i) + for (auto i = 0; i < section.extent<0>(); ++i) { - for (unsigned int j = 0; j < section.extent<1>(); ++j) - for (unsigned int k = 0; k < section.extent<2>(); ++k) + 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]); } @@ -1083,10 +1084,10 @@ SUITE(array_view_tests) TEST(strided_array_view_section_iteration_3d) { int arr[3][4][2]; - for (int i = 0; i < 3; ++i) + for (auto i = 0; i < 3; ++i) { - for (int j = 0; j < 4; ++j) - for (unsigned int k = 0; k < 2; ++k) + for (auto j = 0; j < 4; ++j) + for (auto k = 0; k < 2; ++k) arr[i][j][k] = 8 * i + 2 * j + k; } @@ -1098,11 +1099,11 @@ SUITE(array_view_tests) TEST(dynamic_strided_array_view_section_iteration_3d) { - unsigned int height = 12, width = 2; - unsigned int size = height * width; + auto height = 12, width = 2; + auto size = height * width; auto arr = new int[size]; - for (int unsigned i = 0; i < size; ++i) + for (auto i = 0; i < size; ++i) { arr[i] = i; } @@ -1137,7 +1138,7 @@ SUITE(array_view_tests) X arr[4] = { { 0,1,2 },{ 3,4,5 },{ 6,7,8 },{ 9,10,11 } }; - auto s = sizeof(int) / sizeof(byte); + int s = sizeof(int) / sizeof(byte); auto d2 = 3 * s; auto d1 = sizeof(int) * 12 / d2; @@ -1179,152 +1180,6 @@ SUITE(array_view_tests) } - template - index Convert(index index) - { - return{ index }; - } - - TEST(DomainConverters) - { - // to smaller - { - index<2, int> int_index{ 0,1 }; - index<2, short> short_index{ int_index }; - - CHECK(short_index[0] == 0); - CHECK(short_index[1] == 1); - } - - // to smaller (failure) - { - index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; - CHECK_THROW((Convert<2,int, short int>(big_int_index)), fail_fast); - } - - // to same, sign mismatch - { - index<2, int> int_index{ 0,1 }; - index<2, unsigned int> uint_index{ int_index }; - - CHECK(uint_index[0] == 0); - CHECK(uint_index[1] == 1); - } - - // to same, sign mismatch, reversed - { - index<2, unsigned int> uint_index{ 0,1 }; - index<2, int> int_index{ uint_index }; - - CHECK(int_index[0] == 0); - CHECK(int_index[1] == 1); - } - - // to smaller, sign mismatch - { - index<2, int> int_index{ 0,1 }; - index<2, unsigned short> ushort_index{ int_index }; - - CHECK(ushort_index[0] == 0); - CHECK(ushort_index[1] == 1); - } - - // to bigger - { - index<2, int> int_index{ 0,1 }; - index<2, long long> longlong_index{ int_index }; - - CHECK(longlong_index[0] == 0); - CHECK(longlong_index[1] == 1); - } - - // to bigger with max index - { - index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; - index<2, long long> longlong_index{ big_int_index }; - - CHECK(longlong_index[0] == std::numeric_limits::max()); - CHECK(longlong_index[1] == 1); - } - - // to bigger, sign mismatch - { - index<2, int> int_index{ 0,1 }; - index<2, unsigned long long> ulonglong_index{ int_index }; - - CHECK(ulonglong_index[0] == 0); - CHECK(ulonglong_index[1] == 1); - } - - } - - TEST(DomainConvertersRank1) - { - // to smaller - { - index<1, int> int_index{ 0 }; - index<1, short> short_index{ int_index }; - - CHECK(short_index[0] == 0); - } - - // to smaller (failure) - { - index<1, int> big_int_index{ std::numeric_limits::max() }; - - CHECK_THROW((Convert<1, int, short int>(big_int_index)), fail_fast); - } - - // to same, sign mismatch - { - index<1, int> int_index{ 0 }; - index<1, unsigned int> uint_index{ int_index }; - - CHECK(uint_index[0] == 0); - } - - // to same, sign mismatch, reversed - { - index<1, unsigned int> uint_index{ 0 }; - index<1, int> int_index{ uint_index }; - - CHECK(int_index[0] == 0); - } - - // to smaller, sign mismatch - { - index<1, int> int_index{ 0 }; - index<1, unsigned short> ushort_index{ int_index }; - - CHECK(ushort_index[0] == 0); - } - - // to bigger - { - index<1, int> int_index{ 0 }; - index<1, long long> longlong_index{ int_index }; - - CHECK(longlong_index[0] == 0); - } - - // to bigger with max index - { - index<1, int> big_int_index{ std::numeric_limits::max() }; - index<1, long long> longlong_index{ big_int_index }; - - CHECK(longlong_index[0] == std::numeric_limits::max()); - } - - // to bigger, sign mismatch - { - index<1, int> int_index{ 0 }; - index<1, unsigned long long> ulonglong_index{ int_index }; - - CHECK(ulonglong_index[0] == 0); - } - - } - TEST(constructors) { array_view av(nullptr); @@ -1370,7 +1225,7 @@ SUITE(array_view_tests) int arr[] = {3, 4, 5}; av1 = arr; - array_view, dynamic_range> av2; + array_view av2; av2 = av1; } @@ -1380,21 +1235,21 @@ SUITE(array_view_tests) { array_view av = arr; - CHECK((av.first<2>().bounds() == static_bounds())); + CHECK((av.first<2>().bounds() == static_bounds<2>())); CHECK(av.first<2>().length() == 2); CHECK(av.first(2).length() == 2); } { array_view av = arr; - CHECK((av.first<0>().bounds() == static_bounds())); + CHECK((av.first<0>().bounds() == static_bounds<0>())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } { array_view av = arr; - CHECK((av.first<5>().bounds() == static_bounds())); + CHECK((av.first<5>().bounds() == static_bounds<5>())); CHECK(av.first<5>().length() == 5); CHECK(av.first(5).length() == 5); } @@ -1402,7 +1257,7 @@ SUITE(array_view_tests) { array_view av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - CHECK(av.first<6>().bounds() == static_bounds()); + CHECK(av.first<6>().bounds() == static_bounds<6>()); CHECK(av.first<6>().length() == 6); #endif CHECK_THROW(av.first(6).length(), fail_fast); @@ -1410,7 +1265,7 @@ SUITE(array_view_tests) { array_view av; - CHECK((av.first<0>().bounds() == static_bounds())); + CHECK((av.first<0>().bounds() == static_bounds<0>())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } @@ -1422,21 +1277,21 @@ SUITE(array_view_tests) { array_view av = arr; - CHECK((av.last<2>().bounds() == static_bounds())); + CHECK((av.last<2>().bounds() == static_bounds<2>())); CHECK(av.last<2>().length() == 2); CHECK(av.last(2).length() == 2); } { array_view av = arr; - CHECK((av.last<0>().bounds() == static_bounds())); + CHECK((av.last<0>().bounds() == static_bounds<0>())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } { array_view av = arr; - CHECK((av.last<5>().bounds() == static_bounds())); + CHECK((av.last<5>().bounds() == static_bounds<5>())); CHECK(av.last<5>().length() == 5); CHECK(av.last(5).length() == 5); } @@ -1445,7 +1300,7 @@ SUITE(array_view_tests) { array_view av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - CHECK((av.last<6>().bounds() == static_bounds())); + CHECK((av.last<6>().bounds() == static_bounds<6>())); CHECK(av.last<6>().length() == 6); #endif CHECK_THROW(av.last(6).length(), fail_fast); @@ -1453,7 +1308,7 @@ SUITE(array_view_tests) { array_view av; - CHECK((av.last<0>().bounds() == static_bounds())); + CHECK((av.last<0>().bounds() == static_bounds<0>())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } @@ -1462,12 +1317,12 @@ SUITE(array_view_tests) TEST(custmized_array_view_size) { double (*arr)[3][4] = new double[100][3][4]; - array_view, dynamic_range, 3, 4> av1(arr, (char)10); + array_view av1(arr, 10); struct EffectiveStructure { double* v1; - char v2; + ptrdiff_t v2; }; CHECK(sizeof(av1) == sizeof(EffectiveStructure)); @@ -1483,7 +1338,7 @@ SUITE(array_view_tests) { array_view av = arr; - CHECK((av.sub<2,2>().bounds() == static_bounds())); + CHECK((av.sub<2,2>().bounds() == static_bounds<2>())); CHECK((av.sub<2,2>().length() == 2)); CHECK(av.sub(2,2).length() == 2); CHECK(av.sub(2,3).length() == 3); @@ -1492,14 +1347,14 @@ SUITE(array_view_tests) { array_view av = arr; - CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); } { array_view av = arr; - CHECK((av.sub<0,5>().bounds() == static_bounds())); + CHECK((av.sub<0,5>().bounds() == static_bounds<5>())); CHECK((av.sub<0,5>().length() == 5)); CHECK(av.sub(0,5).length() == 5); CHECK_THROW(av.sub(0,6).length(), fail_fast); @@ -1508,7 +1363,7 @@ SUITE(array_view_tests) { array_view av = arr; - CHECK((av.sub<5,0>().bounds() == static_bounds())); + CHECK((av.sub<5,0>().bounds() == static_bounds<0>())); CHECK((av.sub<5, 0>().length() == 0)); CHECK(av.sub(5,0).length() == 0); CHECK_THROW(av.sub(6,0).length(), fail_fast); @@ -1516,7 +1371,7 @@ SUITE(array_view_tests) { array_view av; - CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); CHECK_THROW((av.sub<1,0>().length()), fail_fast); @@ -1564,7 +1419,7 @@ SUITE(array_view_tests) void AssertContentsMatch(T a1, U a2) { CHECK(a1.length() == a2.length()); - for (size_t i = 0; i < a1.length(); ++i) + for (auto i = 0; i < a1.length(); ++i) CHECK(a1[i] == a2[i]); } @@ -1726,7 +1581,7 @@ SUITE(array_view_tests) CHECK(av1 == av2); - array_view, 20> av3 = av1.as_array_view(dim<>(20)); + array_view av3 = av1.as_array_view(dim<>(20)); CHECK(av3 == av2 && av3 == av1); } -- cgit v1.2.3 From 0cbdc7036d2499967c59c50587242f0611c969b6 Mon Sep 17 00:00:00 2001 From: Matt Newport Date: Mon, 26 Oct 2015 18:23:14 -0700 Subject: Fixed string_view::ensure_z() for const char*. --- include/string_view.h | 6 +++--- tests/string_view_tests.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/string_view.h b/include/string_view.h index 4076d52..7080ce5 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -82,9 +82,9 @@ template array_view ensure_sentinel(const T* seq, SizeType max = std::numeric_limits::max()) { auto cur = seq; - while ((cur - seq) < max && *cur != Sentinel) ++cur; + while (SizeType(cur - seq) < max && *cur != Sentinel) ++cur; fail_fast_assert(*cur == Sentinel); - return{ seq, cur - seq }; + return{ seq, SizeType(cur - seq) }; } @@ -96,7 +96,7 @@ array_view ensure_sentinel(const T* seq, SizeType max = std::n template inline basic_string_view ensure_z(T* const & sz, size_t max = std::numeric_limits::max()) { - return ensure_sentinel<0>(sz, max); + return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp index fb57845..e553ccd 100644 --- a/tests/string_view_tests.cpp +++ b/tests/string_view_tests.cpp @@ -79,6 +79,14 @@ SUITE(string_view_tests) } } + 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"; -- cgit v1.2.3 From b39571781e4ce566de162cb187445b95b56cfd6f Mon Sep 17 00:00:00 2001 From: archshift Date: Mon, 2 Nov 2015 11:47:14 -0800 Subject: array_view: explicitly initialize constexpr function variables --- include/array_view.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 14d8888..69559f6 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -541,7 +541,7 @@ namespace details template 1), typename Ret = std::enable_if_t>> constexpr Ret shift_left(const index& other) noexcept { - Ret ret; + Ret ret{}; for (size_t i = 0; i < Rank - 1; ++i) { ret[i] = other[i + 1]; @@ -887,7 +887,7 @@ public: constexpr bounds_iterator& operator+=(difference_type n) noexcept { auto linear_idx = linearize(curr) + n; - std::remove_const_t stride; + std::remove_const_t stride = 0; stride[rank - 1] = 1; for (size_t i = rank - 1; i-- > 0;) { -- cgit v1.2.3 From 14d50a6f770c95c0e87f1fd77aeea051a61a6882 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 3 Nov 2015 12:44:09 -0800 Subject: Minor compilation fixes and workarounds. --- include/array_view.h | 5 +++-- include/string_view.h | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index efbaa92..83d9231 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -1408,8 +1408,9 @@ public: {} // reshape - template 0)>> - constexpr array_view as_array_view(Dimensions2... dims) + // DimCount here is a workaround for a bug in MSVC 2015 + template 0)>> + constexpr array_view as_array_view(Dimensions2... dims) { using BoundsType = typename array_view::bounds_type; auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); diff --git a/include/string_view.h b/include/string_view.h index b7a1e8a..b5f73c5 100644 --- a/include/string_view.h +++ b/include/string_view.h @@ -78,13 +78,13 @@ using cwstring_view = basic_string_view; // // Will fail-fast if sentinel cannot be found before max elements are examined. // -template -array_view ensure_sentinel(const T* seq, SizeType max = std::numeric_limits::max()) +template +array_view ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; - while (SizeType(cur - seq) < max && *cur != Sentinel) ++cur; + while ((cur - seq) < max && *cur != Sentinel) ++cur; fail_fast_assert(*cur == Sentinel); - return{ seq, SizeType(cur - seq) }; + return{ seq, cur - seq }; } @@ -96,7 +96,7 @@ array_view ensure_sentinel(const T* seq, SizeType max = std::n template inline basic_string_view ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { - return ensure_sentinel(sz, max); + return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -- cgit v1.2.3 From 41517ff316f6740d8b83e5aa8af6f40b1422035e Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 4 Nov 2015 02:11:49 +0000 Subject: Cleaned up size_t use for ranks. Compilation fixes for non-MSVC compilers. --- include/array_view.h | 50 +++++++++++++++++++++++----------------------- tests/array_view_tests.cpp | 5 +++-- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 83d9231..482666e 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -146,7 +146,7 @@ public: constexpr index operator-() const noexcept { index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); + std::transform(ret, ret + rank, ret, std::negate{}); return ret; } @@ -266,7 +266,7 @@ namespace details template struct BoundsRanges { - using size_type = std::ptrdiff_t; + using size_type = std::ptrdiff_t; static const size_type Depth = 0; static const size_type DynamicNum = 0; static const size_type CurrentRange = 1; @@ -278,7 +278,7 @@ namespace details {} BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const size_type* const) { } + BoundsRanges(const std::ptrdiff_t* const) { } BoundsRanges() = default; @@ -312,7 +312,7 @@ namespace details template struct BoundsRanges : BoundsRanges{ using Base = BoundsRanges ; - using size_type = Base::size_type; + using size_type = std::ptrdiff_t; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum + 1; static const size_type CurrentRange = dynamic_range; @@ -321,7 +321,7 @@ namespace details BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const size_type* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { fail_fast_assert(0 <= *arr); } @@ -329,9 +329,9 @@ namespace details BoundsRanges() : m_bound(0) {} template - BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : - Base(static_cast&>(other), false), m_bound(other.totalSize()) - {} + BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : + Base(static_cast&>(other), false), m_bound(other.totalSize()) + {} template void serialize(T& arr) const @@ -386,14 +386,14 @@ namespace details struct BoundsRanges : BoundsRanges { using Base = BoundsRanges ; - using size_type = Base::size_type; + using size_type = std::ptrdiff_t; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum; static const size_type CurrentRange = CurRange; static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const size_type* const arr) : Base(arr) { } + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } BoundsRanges() = default; template @@ -469,10 +469,10 @@ namespace details template struct BoundsRangeConvertible2 : std::true_type {}; - template + template struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) + && (!LessThan::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) {}; template struct BoundsRangeConvertible : std::true_type {}; @@ -561,7 +561,7 @@ public: constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) {} - constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) + constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) { fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); @@ -1230,7 +1230,7 @@ namespace details using size_type = typename Traits::array_view_traits::size_type; }; - template + template struct ArrayViewArrayTraits { using type = array_view; using value_type = T; @@ -1238,7 +1238,7 @@ namespace details using pointer = T*; using reference = T&; }; - template + template struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; template @@ -1292,7 +1292,7 @@ namespace details struct is_array_view_oracle> : std::true_type {}; - template + template struct is_array_view_oracle> : std::true_type {}; @@ -1339,7 +1339,7 @@ public: } // default - template > + template > constexpr array_view() : Base(nullptr, bounds_type()) {} @@ -1351,15 +1351,15 @@ public: // from n-dimensions static array template , - typename = std::enable_if_t::value>> - constexpr array_view (T (&arr)[N]) : Base(arr, Helper::bounds_type()) + typename = std::enable_if_t::value>> + constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) {} // from n-dimensions static array with size template , - typename = std::enable_if_t::value> + typename = std::enable_if_t::value> > - constexpr array_view(T(&arr)[N], size_type size) : Base(arr, Helper::bounds_type{size}) + constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{size}) { fail_fast_assert(size <= N); } @@ -1420,14 +1420,14 @@ public: // to bytes array template >::value> - constexpr auto as_bytes() const noexcept -> array_view + auto as_bytes() const noexcept -> array_view { static_assert(Enabled, "The value_type of array_view must be standarded layout"); return { reinterpret_cast(this->data()), this->bytes() }; } template >::value> - constexpr auto as_writeable_bytes() const noexcept -> array_view + auto as_writeable_bytes() const noexcept -> array_view { static_assert(Enabled, "The value_type of array_view must be standarded layout"); return { reinterpret_cast(this->data()), this->bytes() }; @@ -1435,7 +1435,7 @@ public: // from bytes array template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> + constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); @@ -1444,7 +1444,7 @@ public: } template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> + constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> { static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 8937388..d9d0a42 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -222,12 +222,13 @@ SUITE(array_view_tests) #endif } } - template void fn(Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } + + template void fn(const Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } TEST (array_view_reshape_test) { int a[3][4][5]; auto av = as_array_view(a); - fn(av.bounds()); + fn(av.bounds()); auto av2 = av.as_array_view(dim<60>()); auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); -- cgit v1.2.3 From e9a9602b4f4111dfcf2da445852b6515ba26db3a Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 3 Nov 2015 18:56:55 -0800 Subject: Fixed compile issues for MSVC 2013. --- include/array_view.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index 482666e..bca973a 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -43,6 +43,10 @@ // VS 2013 workarounds #if _MSC_VER <= 1800 +#pragma push_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG + + // noexcept is not understood #ifndef GSL_THROWS_FOR_TESTING #define noexcept /* nothing */ @@ -106,9 +110,19 @@ public: std::copy(values, values + Rank, elems); } - template::value, typename Dummy = std::enable_if_t> - constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } - {} +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) + {} +#else + template::value>> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} +#endif constexpr index(const index& other) noexcept = default; @@ -1957,6 +1971,10 @@ general_array_view_iterator operator+(typename general_array_view_ite #pragma undef noexcept #endif // GSL_THROWS_FOR_TESTING +#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#pragma pop_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") + + #endif // _MSC_VER <= 1800 #endif // _MSC_VER -- cgit v1.2.3 From 68064d63fefc27caa5ff2036599bf2baba23736d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 3 Nov 2015 19:17:11 -0800 Subject: Converted all tabs to spaces in array_view.h. --- include/array_view.h | 3053 +++++++++++++++++++++++++------------------------- 1 file changed, 1527 insertions(+), 1526 deletions(-) diff --git a/include/array_view.h b/include/array_view.h index bca973a..cbc33be 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -73,192 +73,192 @@ namespace gsl { */ namespace details { - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; - template - class are_integral : public std::integral_constant {}; + template + class are_integral : public std::integral_constant {}; - template - class are_integral : public std::integral_constant::value && are_integral::value> {}; + template + class are_integral : public std::integral_constant::value && are_integral::value> {}; } template class index final { - static_assert(Rank > 0, "Rank must be greater than 0!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); - template - friend class index; + template + friend class index; public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index() noexcept - {} + constexpr index() noexcept + {} - constexpr index(const value_type(&values)[Rank]) noexcept - { - std::copy(values, values + Rank, elems); - } + constexpr index(const value_type(&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } #ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template::value && details::are_integral::value>> constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) {} #else - template::value>> - constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } {} #endif - constexpr index(const index& other) noexcept = default; - - constexpr index& operator=(const index& rhs) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const noexcept - { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); - return elems[component_idx]; - } - - constexpr bool operator==(const index& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const index& rhs) const noexcept - { - return !(this == rhs); - } - - constexpr index operator+() const noexcept - { - return *this; - } - - constexpr index operator-() const noexcept - { - index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr index operator+(const index& rhs) const noexcept - { - index ret = *this; - ret += rhs; - return ret; - } - - constexpr index operator-(const index& rhs) const noexcept - { - index ret = *this; - ret -= rhs; - return ret; - } - - constexpr index& operator+=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr index operator*(value_type v) const noexcept - { - index ret = *this; - ret *= v; - return ret; - } - - constexpr index operator/(value_type v) const noexcept - { - index ret = *this; - ret /= v; - return ret; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return rhs * v; - } - - constexpr index& operator*=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept + { + return !(this == rhs); + } + + constexpr index operator+() const noexcept + { + return *this; + } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } private: - value_type elems[Rank] = {}; + value_type elems[Rank] = {}; }; #ifndef _MSC_VER struct static_bounds_dynamic_range_t { - template ::value>> - constexpr operator T() const noexcept - { - return static_cast(-1); - } - - template ::value>> - constexpr bool operator ==(T other) const noexcept - { - return static_cast(-1) == other; - } - - template ::value>> - constexpr bool operator !=(T other) const noexcept - { - return static_cast(-1) != other; - } + template ::value>> + constexpr operator T() const noexcept + { + return static_cast(-1); + } + + template ::value>> + constexpr bool operator ==(T other) const noexcept + { + return static_cast(-1) == other; + } + + template ::value>> + constexpr bool operator !=(T other) const noexcept + { + return static_cast(-1) != other; + } }; template ::value>> constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept { - return right == left; + return right == left; } template ::value>> constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept { - return right != left; + return right != left; } constexpr static_bounds_dynamic_range_t dynamic_range{}; @@ -272,263 +272,263 @@ struct contiguous_mapping_tag : generalized_mapping_tag {}; namespace details { - template - struct LessThan - { - static const bool value = Left < Right; - }; + template + struct LessThan + { + static const bool value = Left < Right; + }; - template - struct BoundsRanges { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; + template + struct BoundsRanges { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; // TODO : following signature is for work around VS bug template - BoundsRanges(const OtherRange&, bool /* firstLevel */) + BoundsRanges(const OtherRange&, bool /* firstLevel */) {} - - BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) { } - BoundsRanges() = default; + + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) { } + BoundsRanges() = default; - template - void serialize(T&) const + template + void serialize(T&) const {} - template - size_type linearize(const T&) const + template + size_type linearize(const T&) const { - return 0; - } + return 0; + } + + template + bool contains(const T&) const + { + return 0; + } - template - bool contains(const T&) const + size_type totalSize() const noexcept { - return 0; - } + return TotalSize; + } - size_type totalSize() const noexcept + bool operator==(const BoundsRanges&) const noexcept { - return TotalSize; - } - - bool operator==(const BoundsRanges&) const noexcept - { - return true; - } - }; - - template - struct BoundsRanges : BoundsRanges{ - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges (const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - fail_fast_assert(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : - Base(static_cast&>(other), false), m_bound(other.totalSize()) - {} - - template - void serialize(T& arr) const + return true; + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges (const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + fail_fast_assert(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : + Base(static_cast&>(other), false), m_bound(other.totalSize()) + {} + + template + void serialize(T& arr) const { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } - template - size_type linearize(const T& arr) const + template + size_type linearize(const T& arr) const { - const size_type index = this->Base::totalSize() * arr[Dim]; - fail_fast_assert(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T & arr) const + const size_type index = this->Base::totalSize() * arr[Dim]; + fail_fast_assert(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T & arr) const { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) - return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const noexcept + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept { - return m_bound; - } - + return m_bound; + } + size_type elementNum() const noexcept { - return totalSize() / this->Base::totalSize(); - } - + return totalSize() / this->Base::totalSize(); + } + size_type elementNum(size_t dim) const noexcept { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator == (const BoundsRanges & rhs) const noexcept - { - return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) - { - fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); - } - - template - void serialize(T& arr) const + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const noexcept + { + return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges ; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } + fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + } - template - size_type linearize(const T& arr) const + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const { - fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); - return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); - } + fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); + return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); + } - template + template size_type contains(const T& arr) const { - if (arr[Dim] >= CurrentRange) - return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) - return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const noexcept + if (arr[Dim] >= CurrentRange) + return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) + return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept { - return CurrentRange * this->Base::totalSize(); - } + return CurrentRange * this->Base::totalSize(); + } size_type elementNum() const noexcept { - return CurrentRange; - } + return CurrentRange; + } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator== (const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible2; + + template > + auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant())) + {}; + + template + struct BoundsRangeConvertible2 : std::true_type {}; + + template + struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) + {}; + template + struct BoundsRangeConvertible : std::true_type {}; + + template + struct TypeListIndexer + { + const TypeChain & obj; + TypeListIndexer(const TypeChain & obj) :obj(obj){} + template + const TypeChain & getObj(std::true_type) + { + return obj; + } + template + auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) + { + return TypeListIndexer(static_cast(obj)).template get(); + } + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain &obj) + { + return TypeListIndexer(obj); + } - size_type elementNum(size_t dim) const noexcept + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator== (const BoundsRanges& rhs) const noexcept - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible2; - - template > - auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant())) - {}; - - template - struct BoundsRangeConvertible2 : std::true_type {}; - - template - struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) - {}; - template - struct BoundsRangeConvertible : std::true_type {}; - - template - struct TypeListIndexer - { - const TypeChain & obj; - TypeListIndexer(const TypeChain & obj) :obj(obj){} - template - const TypeChain & getObj(std::true_type) - { - return obj; - } - template - auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) - { - return TypeListIndexer(static_cast(obj)).template get(); - } - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain &obj) - { - return TypeListIndexer(obj); - } - - template 1), typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) - { - ret[i] = other[i + 1]; - } - return ret; - } + ret[i] = other[i + 1]; + } + return ret; + } } template @@ -538,242 +538,242 @@ template class static_bounds { public: - static_bounds(const details::BoundsRanges&) { - } + static_bounds(const details::BoundsRanges&) { + } }; template class static_bounds { - using MyRanges = details::BoundsRanges; + using MyRanges = details::BoundsRanges; - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} - - template - friend class static_bounds; + + template + friend class static_bounds; public: - static const size_t rank = MyRanges::Depth; - static const size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; - using size_type = std::ptrdiff_t; - using index_type = index; + using size_type = std::ptrdiff_t; + using index_type = index; using const_index_type = std::add_const_t; using iterator = bounds_iterator; using const_iterator = bounds_iterator; using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; - constexpr static_bounds(const static_bounds&) = default; - - template , details::BoundsRanges >::value>> + constexpr static_bounds(const static_bounds&) = default; + + template , details::BoundsRanges >::value>> constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) {} - constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) - { - fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); - fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); - } - - constexpr static_bounds() = default; - - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new(&m_ranges) MyRanges (otherBounds.m_ranges); - return *this; - } - - constexpr sliced_type slice() const noexcept - { - return sliced_type{static_cast &>(m_ranges)}; - } - - constexpr size_type stride() const noexcept - { - return rank > 1 ? slice().size() : 1; - } - - constexpr size_type size() const noexcept - { - return m_ranges.totalSize(); - } - - constexpr size_type total_size() const noexcept - { - return m_ranges.totalSize(); - } - - constexpr size_type linearize(const index_type & idx) const - { - return m_ranges.linearize(idx); - } - - constexpr bool contains(const index_type& idx) const noexcept - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](size_t index) const noexcept - { - return m_ranges.elementNum(index); - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - constexpr index_type index_bounds() const noexcept - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return{ extents }; - } - - template - constexpr bool operator == (const static_bounds& rhs) const noexcept - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator != (const static_bounds& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const noexcept - { - return const_iterator(*this, index_type{}); - } - - constexpr const_iterator end() const noexcept - { - return const_iterator(*this, this->index_bounds()); - } + constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) + { + fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); + fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new(&m_ranges) MyRanges (otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast &>(m_ranges)}; + } + + constexpr size_type stride() const noexcept + { + return rank > 1 ? slice().size() : 1; + } + + constexpr size_type size() const noexcept + { + return m_ranges.totalSize(); + } + + constexpr size_type total_size() const noexcept + { + return m_ranges.totalSize(); + } + + constexpr size_type linearize(const index_type & idx) const + { + return m_ranges.linearize(idx); + } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return{ extents }; + } + + template + constexpr bool operator == (const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator != (const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept + { + return const_iterator(*this, index_type{}); + } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } }; template class strided_bounds { - template - friend class strided_bounds; + template + friend class strided_bounds; public: - static const size_t rank = Rank; + static const size_t rank = Rank; using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = index; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; using const_index_type = std::add_const_t; using iterator = bounds_iterator; using const_iterator = bounds_iterator; static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds &) noexcept = default; + constexpr strided_bounds(const strided_bounds &) noexcept = default; constexpr strided_bounds(const value_type(&values)[rank], index_type strides) : m_extents(values), m_strides(std::move(strides)) {} - constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept - : m_extents(extents), m_strides(strides) - {} - - constexpr index_type strides() const noexcept - { - return m_strides; - } - - constexpr size_type total_size() const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; ++i) - { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - + constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept + : m_extents(extents), m_strides(strides) + {} + + constexpr index_type strides() const noexcept + { + return m_strides; + } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) + { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + constexpr size_type size() const noexcept - { - size_type ret = 1; - for (size_t i = 0; i < rank; ++i) - { - ret *= m_extents[i]; - } - return ret; - } - + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) + { + ret *= m_extents[i]; + } + return ret; + } + constexpr bool contains(const index_type& idx) const noexcept - { - for (size_t i = 0; i < rank; ++i) - { - if (idx[i] < 0 || idx[i] >= m_extents[i]) - return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; i++) - { - fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); - ret += idx[i] * m_strides[i]; - } - return ret; - } - + { + for (size_t i = 0; i < rank; ++i) + { + if (idx[i] < 0 || idx[i] >= m_extents[i]) + return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) + { + fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); + ret += idx[i] * m_strides[i]; + } + return ret; + } + constexpr size_type stride() const noexcept - { - return m_strides[0]; - } - + { + return m_strides[0]; + } + template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return{ details::shift_left(m_extents), details::shift_left(m_strides) }; - } - + constexpr sliced_type slice() const + { + return{ details::shift_left(m_extents), details::shift_left(m_strides) }; + } + template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + constexpr index_type index_bounds() const noexcept - { - return m_extents; - } - constexpr const_iterator begin() const noexcept - { - return const_iterator{ *this, index_type{} }; - } - - constexpr const_iterator end() const noexcept - { - return const_iterator{ *this, index_bounds() }; - } + { + return m_extents; + } + constexpr const_iterator begin() const noexcept + { + return const_iterator{ *this, index_type{} }; + } + + constexpr const_iterator end() const noexcept + { + return const_iterator{ *this, index_bounds() }; + } private: - index_type m_extents; - index_type m_strides; + index_type m_extents; + index_type m_strides; }; template @@ -787,214 +787,214 @@ template class bounds_iterator: public std::iterator { private: - using Base = std::iterator ; + using Base = std::iterator ; public: - static const size_t rank = IndexType::rank; - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary(bnd.index_bounds()), curr(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const noexcept - { - return curr; - } - - constexpr pointer operator->() const noexcept - { - return &curr; - } - - constexpr bounds_iterator& operator++() noexcept - { - for (size_t i = rank; i-- > 0;) - { - if (curr[i] < boundary[i] - 1) - { - curr[i]++; - return *this; - } - curr[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr = boundary; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - if (!less(curr, boundary)) - { - // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) - { - curr[i] = boundary[i] - 1; - } - return *this; - } - for (size_t i = rank; i-- > 0;) - { - if (curr[i] >= 1) - { - curr[i]--; - return *this; - } - curr[i] = boundary[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - fail_fast_assert(false); - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - auto linear_idx = linearize(curr) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) - { - stride[i] = stride[i + 1] * boundary[i + 1]; - } - for (size_t i = 0; i < rank; ++i) - { - curr[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return linearize(curr) - linearize(rhs.curr); - } - - constexpr value_type operator[](difference_type n) const noexcept - { - return *(*this + n); - } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr == rhs.curr; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return less(curr, rhs.curr); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(bounds_iterator& rhs) noexcept - { - std::swap(boundary, rhs.boundary); - std::swap(curr, rhs.curr); - } + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary(bnd.index_bounds()), curr(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept + { + return curr; + } + + constexpr pointer operator->() const noexcept + { + return &curr; + } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) + { + if (curr[i] < boundary[i] - 1) + { + curr[i]++; + return *this; + } + curr[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr = boundary; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr, boundary)) + { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) + { + curr[i] = boundary[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) + { + if (curr[i] >= 1) + { + curr[i]--; + return *this; + } + curr[i] = boundary[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + fail_fast_assert(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{ *this }; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) + { + stride[i] = stride[i + 1] * boundary[i + 1]; + } + for (size_t i = 0; i < rank; ++i) + { + curr[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{ *this }; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr) - linearize(rhs.curr); + } + + constexpr value_type operator[](difference_type n) const noexcept + { + return *(*this + n); + } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr == rhs.curr; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr, rhs.curr); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept + { + return rhs < *this; + } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary, rhs.boundary); + std::swap(curr, rhs.curr); + } private: - constexpr bool less(index_type& one, index_type& other) const noexcept - { - for (size_t i = 0; i < rank; ++i) - { - if (one[i] < other[i]) - return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const noexcept - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary)) - { - res = 1; - for (size_t i = rank; i-- > 0;) - { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary[i]; - } - } - else - { - for (size_t i = rank; i-- > 0;) - { - res += idx[i] * multiplier; - multiplier *= boundary[i]; - } - } - return res; - } - - value_type boundary; - std::remove_const_t curr; + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) + { + if (one[i] < other[i]) + return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary)) + { + res = 1; + for (size_t i = rank; i-- > 0;) + { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) + { + res += idx[i] * multiplier; + multiplier *= boundary[i]; + } + } + return res; + } + + value_type boundary; + std::remove_const_t curr; }; template bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept { - return rhs + n; + return rhs + n; } // @@ -1002,35 +1002,35 @@ bounds_iterator operator+(typename bounds_iterator::differ // namespace details { - template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) - { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return{ stride }; - } - - template - void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) - { - static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); - static_assert(std::is_same::value, "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); - fail_fast_assert(src.size() == dest.size()); - } + template + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) + { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return{ stride }; + } + + template + void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) + { + static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); + static_assert(std::is_same::value, "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); + fail_fast_assert(src.size() == dest.size()); + } } // namespace details @@ -1045,181 +1045,181 @@ template class basic_array_view { public: - static const size_t rank = BoundsType::rank; - using bounds_type = BoundsType; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = ValueType*; - using reference = ValueType&; - using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; - using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = std::conditional_t>; + static const size_t rank = BoundsType::rank; + using bounds_type = BoundsType; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = ValueType*; + using reference = ValueType&; + using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; private: - pointer m_pdata; - bounds_type m_bounds; + pointer m_pdata; + bounds_type m_bounds; public: - constexpr bounds_type bounds() const noexcept - { - return m_bounds; - } - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_bounds.template extent(); - } - constexpr size_type size() const noexcept - { - return m_bounds.size(); - } - constexpr reference operator[](const index_type& idx) const - { - return m_pdata[m_bounds.linearize(idx)]; - } - constexpr pointer data() const noexcept - { - return m_pdata; - } - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); - const size_type ridx = idx * m_bounds.stride(); - - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); - return Ret {m_pdata + ridx, m_bounds.slice()}; - } - - constexpr operator bool () const noexcept - { - return m_pdata != nullptr; - } - - constexpr iterator begin() const - { - return iterator {this, true}; - } - constexpr iterator end() const - { - return iterator {this, false}; - } - constexpr const_iterator cbegin() const - { - return const_iterator {reinterpret_cast *>(this), true}; - } - constexpr const_iterator cend() const - { - return const_iterator {reinterpret_cast *>(this), false}; - } - - constexpr reverse_iterator rbegin() const - { - return reverse_iterator {end()}; - } - constexpr reverse_iterator rend() const - { - return reverse_iterator {begin()}; - } - constexpr const_reverse_iterator crbegin() const - { - return const_reverse_iterator {cend()}; - } - constexpr const_reverse_iterator crend() const - { - return const_reverse_iterator {cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator== (const basic_array_view & other) const noexcept - { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!= (const basic_array_view & other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator< (const basic_array_view & other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<= (const basic_array_view & other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator> (const basic_array_view & other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>= (const basic_array_view & other) const noexcept - { - return !(*this < other); - } + constexpr bounds_type bounds() const noexcept + { + return m_bounds; + } + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + return m_bounds.template extent(); + } + constexpr size_type size() const noexcept + { + return m_bounds.size(); + } + constexpr reference operator[](const index_type& idx) const + { + return m_pdata[m_bounds.linearize(idx)]; + } + constexpr pointer data() const noexcept + { + return m_pdata; + } + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + const size_type ridx = idx * m_bounds.stride(); + + fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + return Ret {m_pdata + ridx, m_bounds.slice()}; + } + + constexpr operator bool () const noexcept + { + return m_pdata != nullptr; + } + + constexpr iterator begin() const + { + return iterator {this, true}; + } + constexpr iterator end() const + { + return iterator {this, false}; + } + constexpr const_iterator cbegin() const + { + return const_iterator {reinterpret_cast *>(this), true}; + } + constexpr const_iterator cend() const + { + return const_iterator {reinterpret_cast *>(this), false}; + } + + constexpr reverse_iterator rbegin() const + { + return reverse_iterator {end()}; + } + constexpr reverse_iterator rend() const + { + return reverse_iterator {begin()}; + } + constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator {cend()}; + } + constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator {cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator== (const basic_array_view & other) const noexcept + { + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!= (const basic_array_view & other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator< (const basic_array_view & other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<= (const basic_array_view & other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator> (const basic_array_view & other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>= (const basic_array_view & other) const noexcept + { + return !(*this < other); + } public: - template ::value - && std::is_convertible::value>> - constexpr basic_array_view(const basic_array_view & other ) noexcept - : m_pdata(other.m_pdata), m_bounds(other.m_bounds) - { - } + template ::value + && std::is_convertible::value>> + constexpr basic_array_view(const basic_array_view & other ) noexcept + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) + { + } protected: - constexpr basic_array_view(pointer data, bounds_type bound) noexcept - : m_pdata(data) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept - : m_pdata(reinterpret_cast(data)) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_array_view as_array_view(const DestBounds &bounds) - { - details::verifyBoundsReshape(m_bounds, bounds); - return {m_pdata, bounds}; - } + constexpr basic_array_view(pointer data, bounds_type bound) noexcept + : m_pdata(data) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept + : m_pdata(reinterpret_cast(data)) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + constexpr basic_array_view as_array_view(const DestBounds &bounds) + { + details::verifyBoundsReshape(m_bounds, bounds); + return {m_pdata, bounds}; + } private: - friend iterator; - friend const_iterator; - template - friend class basic_array_view; + friend iterator; + friend const_iterator; + template + friend class basic_array_view; }; template struct dim { - static const std::ptrdiff_t value = DimSize; + static const std::ptrdiff_t value = DimSize; }; template <> struct dim { - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - dim(std::ptrdiff_t size) : dvalue(size) {} + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim(std::ptrdiff_t size) : dvalue(size) {} }; template @@ -1230,89 +1230,89 @@ class strided_array_view; namespace details { - template - struct ArrayViewTypeTraits - { - using value_type = T; - using size_type = size_t; - }; - - template - struct ArrayViewTypeTraits::type> - { - using value_type = typename Traits::array_view_traits::value_type; - using size_type = typename Traits::array_view_traits::size_type; - }; - - template - struct ArrayViewArrayTraits { - using type = array_view; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - fail_fast_assert(totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - fail_fast_assert(BoundsType::static_size == totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl(totalSize, std::integral_constant()); - } - - struct Sep{}; - - template - T static_as_array_view_helper(Sep, Args... args) - { - return T{static_cast(args)...}; - } - template - std::enable_if_t>::value && !std::is_same::value, T> static_as_array_view_helper(Arg, Args... args) - { - return static_as_array_view_helper(args...); - } - template - T static_as_array_view_helper(dim val, Args ... args) - { - return static_as_array_view_helper(args..., val.dvalue); - } - - template - struct static_as_array_view_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_array_view_oracle : std::false_type - {}; - - template - struct is_array_view_oracle> : std::true_type - {}; - + template + struct ArrayViewTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct ArrayViewTypeTraits::type> + { + using value_type = typename Traits::array_view_traits::value_type; + using size_type = typename Traits::array_view_traits::size_type; + }; + + template + struct ArrayViewArrayTraits { + using type = array_view; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + fail_fast_assert(totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + fail_fast_assert(BoundsType::static_size == totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl(totalSize, std::integral_constant()); + } + + struct Sep{}; + + template + T static_as_array_view_helper(Sep, Args... args) + { + return T{static_cast(args)...}; + } + template + std::enable_if_t>::value && !std::is_same::value, T> static_as_array_view_helper(Arg, Args... args) + { + return static_as_array_view_helper(args...); + } + template + T static_as_array_view_helper(dim val, Args ... args) + { + return static_as_array_view_helper(args..., val.dvalue); + } + + template + struct static_as_array_view_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_array_view_oracle : std::false_type + {}; + + template + struct is_array_view_oracle> : std::true_type + {}; + template - struct is_array_view_oracle> : std::true_type - {}; - + struct is_array_view_oracle> : std::true_type + {}; + template - struct is_array_view : is_array_view_oracle> - {}; + struct is_array_view : is_array_view_oracle> + {}; } @@ -1320,265 +1320,266 @@ namespace details template class array_view : public basic_array_view > { - template - friend class array_view; + template + friend class array_view; - using Base = basic_array_view>; + using Base = basic_array_view>; public: - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - using Base::rank; + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; + using typename Base::reference; + using Base::rank; public: - // basic + // basic constexpr array_view(pointer ptr, size_type size) : Base(ptr, bounds_type{ size }) {} constexpr array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) {} - constexpr array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) - {} + constexpr array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) + {} - constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) - { - fail_fast_assert(size == 0); - } + constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + { + fail_fast_assert(size == 0); + } - // default - template > - constexpr array_view() : Base(nullptr, bounds_type()) - {} + // default + template > + constexpr array_view() : Base(nullptr, bounds_type()) + {} - // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template + // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) + template /*typename Dummy = std::enable_if_t::value>*/> - constexpr array_view(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) - {} + constexpr array_view(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) + {} - // from n-dimensions static array - template , + // from n-dimensions static array + template , typename = std::enable_if_t::value>> - constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) - {} + constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + {} - // from n-dimensions static array with size - template , - typename = std::enable_if_t::value> + // from n-dimensions static array with size + template , + typename = std::enable_if_t::value> > constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{size}) - { - fail_fast_assert(size <= N); - } + { + fail_fast_assert(size <= N); + } - // from std array - template , typename Base::bounds_type>::value> > - constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) - {} + constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} - template , typename Base::bounds_type>::value && std::is_const::value> > - constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) - {} + constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} - // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity - template ::value - && details::LessThan::value> + // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity + template ::value + && details::LessThan::value> > // remove literal 0 case - constexpr array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) - {} - - // from containers. It must has .size() and .data() two function signatures - template ::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> - > - constexpr array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) - {} - - constexpr array_view(const array_view &) = default; - - // convertible - template >, - typename OtherBaseType = basic_array_view>, - typename Dummy = std::enable_if_t::value> - > - constexpr array_view(const array_view &av) + constexpr array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + {} + + // from containers. It must has .size() and .data() two function signatures + template ::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + constexpr array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + {} + + constexpr array_view(const array_view &) = default; + + // convertible + template >, + typename OtherBaseType = basic_array_view>, + typename Dummy = std::enable_if_t::value> + > + constexpr array_view(const array_view &av) : Base(static_cast::Base&>(av)) {} - // reshape + // reshape // DimCount here is a workaround for a bug in MSVC 2015 template 0)>> constexpr array_view as_array_view(Dimensions2... dims) - { - using BoundsType = typename array_view::bounds_type; - auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(this->bounds(), tobounds); - return {this->data(), tobounds}; - } - - // to bytes array - template >::value> - auto as_bytes() const noexcept -> array_view - { - static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; - } - - template >::value> - auto as_writeable_bytes() const noexcept -> array_view - { - static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; - } + { + using BoundsType = typename array_view::bounds_type; + auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(this->bounds(), tobounds); + return {this->data(), tobounds}; + } + + // to bytes array + template >::value> + auto as_bytes() const noexcept -> array_view + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + template >::value> + auto as_writeable_bytes() const noexcept -> array_view + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } // from bytes array - template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> - { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; - } - - template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> - { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; - } - - // section on linear space - template - constexpr array_view first() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed - return { this->data(), Count }; - } - - constexpr array_view first(size_type count) const noexcept - { - fail_fast_assert(count <= this->size()); - return { this->data(), count }; - } - - template - constexpr array_view last() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); - return { this->data() + this->size() - Count, Count }; - } - - constexpr array_view last(size_type count) const noexcept - { - fail_fast_assert(count <= this->size()); - return { this->data() + this->size() - count, count }; - } - - template - constexpr array_view sub() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); - return { this->data() + Offset, Count }; - } - - constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept - { - fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); - return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; - } - - // size - constexpr size_type length() const noexcept - { - return this->size(); - } - - constexpr size_type used_length() const noexcept - { - return length(); - } - - constexpr size_type bytes() const noexcept - { - return sizeof(value_type) * this->size(); - } - - constexpr size_type used_bytes() const noexcept - { - return bytes(); - } - - // section - constexpr strided_array_view section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; - } - - constexpr reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } - - template 1), typename Dummy = std::enable_if_t> - constexpr array_view operator[](size_type idx) const - { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds() }; - } - - using Base::operator==; - using Base::operator!=; - using Base::operator<; - using Base::operator<=; - using Base::operator>; - using Base::operator>=; + template::value, typename = std::enable_if_t> + constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + } + + template::value, typename = std::enable_if_t> + constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + } + + // section on linear space + template + constexpr array_view first() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + return { this->data(), Count }; + } + + constexpr array_view first(size_type count) const noexcept + { + fail_fast_assert(count <= this->size()); + return { this->data(), count }; + } + + template + constexpr array_view last() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + return { this->data() + this->size() - Count, Count }; + } + + constexpr array_view last(size_type count) const noexcept + { + fail_fast_assert(count <= this->size()); + return { this->data() + this->size() - count, count }; + } + + template + constexpr array_view sub() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); + return { this->data() + Offset, Count }; + } + + constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept + { + fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); + return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; + } + + // size + constexpr size_type length() const noexcept + { + return this->size(); + } + + constexpr size_type used_length() const noexcept + { + return length(); + } + + constexpr size_type bytes() const noexcept + { + return sizeof(value_type) * this->size(); + } + + constexpr size_type used_bytes() const noexcept + { + return bytes(); + } + + // section + constexpr strided_array_view section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + } + + constexpr reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + + template 1), typename Dummy = std::enable_if_t> + constexpr array_view operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds() }; + } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; + using Base::operator<=; + using Base::operator>; + using Base::operator>=; }; template constexpr auto as_array_view(T* const& ptr, dim... args) -> array_view, Dimensions...> { - return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; + return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; } template constexpr auto as_array_view (T* arr, std::ptrdiff_t len) -> typename details::ArrayViewArrayTraits::type { - return {reinterpret_cast*>(arr), len}; + return {reinterpret_cast*>(arr), len}; } template constexpr auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type { - return {arr}; + return {arr}; } template constexpr array_view as_array_view(const std::array &arr) { - return {arr}; + return {arr}; } template @@ -1587,374 +1588,374 @@ constexpr array_view as_array_view(const std::array &&) = dele template constexpr array_view as_array_view(std::array &arr) { - return {arr}; + return {arr}; } template constexpr array_view as_array_view(T *begin, T *end) { - return {begin, end}; + return {begin, end}; } template constexpr auto as_array_view(Cont &arr) -> std::enable_if_t>::value, - array_view, dynamic_range>> + array_view, dynamic_range>> { fail_fast_assert(arr.size() < PTRDIFF_MAX); - return {arr.data(), static_cast(arr.size())}; + return {arr.data(), static_cast(arr.size())}; } template constexpr auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, - array_view, dynamic_range>> = delete; + array_view, dynamic_range>> = delete; template class strided_array_view : public basic_array_view> { - using Base = basic_array_view>; + using Base = basic_array_view>; - template - friend class strided_array_view; + template + friend class strided_array_view; public: - using Base::rank; - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - - // from static array of size N - template - strided_array_view(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); - } - - // from raw data - strided_array_view(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); - } - - // from array view - template > - strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); - } - - // convertible - template >, - typename OtherBaseType = basic_array_view>, - typename Dummy = std::enable_if_t::value> - > + using Base::rank; + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; + using typename Base::reference; + + // from static array of size N + template + strided_array_view(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); + } + + // from raw data + strided_array_view(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); + } + + // from array view + template > + strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); + } + + // convertible + template >, + typename OtherBaseType = basic_array_view>, + typename Dummy = std::enable_if_t::value> + > constexpr strided_array_view(const strided_array_view &av) : Base(static_cast::Base &>(av)) // static_cast is required {} - // convert from bytes - template - strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; - } - - strided_array_view section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } - - template 1), typename Dummy = std::enable_if_t> - constexpr strided_array_view operator[](size_type idx) const - { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; - } + // convert from bytes + template + strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; + } + + strided_array_view section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + + template 1), typename Dummy = std::enable_if_t> + constexpr strided_array_view operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; + } private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); - index_type ret = extent; - ret[rank - 1] /= d; + index_type ret = extent; + ret[rank - 1] /= d; - return ret; - } + return ret; + } - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) - { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - return strides; - } + return strides; + } - template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); - for (size_t i = rank - 1; i > 0; --i) - fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); + for (size_t i = rank - 1; i > 0; --i) + fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); - index_type ret = strides / d; - ret[rank - 1] = 1; + index_type ret = strides / d; + ret[rank - 1] = 1; - return ret; - } + return ret; + } }; template class contiguous_array_view_iterator : public std::iterator { - using Base = std::iterator; + using Base = std::iterator; public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; private: - template - friend class basic_array_view; + template + friend class basic_array_view; pointer m_pdata; - const ArrayView * m_validator; - void validateThis() const - { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); - } - contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} + const ArrayView * m_validator; + void validateThis() const + { + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); + } + contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} public: - reference operator*() const noexcept - { - validateThis(); - return *m_pdata; - } - pointer operator->() const noexcept - { - validateThis(); - return m_pdata; - } - contiguous_array_view_iterator& operator++() noexcept - { - ++m_pdata; - return *this; - } - contiguous_array_view_iterator operator++(int)noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_array_view_iterator& operator--() noexcept - { - --m_pdata; - return *this; - } - contiguous_array_view_iterator operator--(int)noexcept - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_array_view_iterator operator+(difference_type n) const noexcept - { - contiguous_array_view_iterator ret{ *this }; - return ret += n; - } - contiguous_array_view_iterator& operator+=(difference_type n) noexcept - { - m_pdata += n; - return *this; - } - contiguous_array_view_iterator operator-(difference_type n) const noexcept - { - contiguous_array_view_iterator ret{ *this }; - return ret -= n; - } - contiguous_array_view_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - difference_type operator-(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata - rhs.m_pdata; - } - reference operator[](difference_type n) const noexcept - { - return *(*this + n); - } - bool operator==(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata == rhs.m_pdata; - } - bool operator!=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - bool operator<(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata < rhs.m_pdata; - } - bool operator<=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const contiguous_array_view_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - void swap(contiguous_array_view_iterator& rhs) noexcept - { - std::swap(m_pdata, rhs.m_pdata); - std::swap(m_validator, rhs.m_validator); - } + reference operator*() const noexcept + { + validateThis(); + return *m_pdata; + } + pointer operator->() const noexcept + { + validateThis(); + return m_pdata; + } + contiguous_array_view_iterator& operator++() noexcept + { + ++m_pdata; + return *this; + } + contiguous_array_view_iterator operator++(int)noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_array_view_iterator& operator--() noexcept + { + --m_pdata; + return *this; + } + contiguous_array_view_iterator operator--(int)noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_array_view_iterator operator+(difference_type n) const noexcept + { + contiguous_array_view_iterator ret{ *this }; + return ret += n; + } + contiguous_array_view_iterator& operator+=(difference_type n) noexcept + { + m_pdata += n; + return *this; + } + contiguous_array_view_iterator operator-(difference_type n) const noexcept + { + contiguous_array_view_iterator ret{ *this }; + return ret -= n; + } + contiguous_array_view_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + difference_type operator-(const contiguous_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata - rhs.m_pdata; + } + reference operator[](difference_type n) const noexcept + { + return *(*this + n); + } + bool operator==(const contiguous_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata == rhs.m_pdata; + } + bool operator!=(const contiguous_array_view_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + bool operator<(const contiguous_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata < rhs.m_pdata; + } + bool operator<=(const contiguous_array_view_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + bool operator>(const contiguous_array_view_iterator& rhs) const noexcept + { + return rhs < *this; + } + bool operator>=(const contiguous_array_view_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + void swap(contiguous_array_view_iterator& rhs) noexcept + { + std::swap(m_pdata, rhs.m_pdata); + std::swap(m_validator, rhs.m_validator); + } }; template contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) noexcept { - return rhs + n; + return rhs + n; } template class general_array_view_iterator : public std::iterator { - using Base = std::iterator; + using Base = std::iterator; public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; private: - template - friend class basic_array_view; - + template + friend class basic_array_view; + const ArrayView * m_container; - typename ArrayView::bounds_type::iterator m_itr; - general_array_view_iterator(const ArrayView *container, bool isbegin) : - m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - {} + typename ArrayView::bounds_type::iterator m_itr; + general_array_view_iterator(const ArrayView *container, bool isbegin) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + {} public: - reference operator*() noexcept - { - return (*m_container)[*m_itr]; - } - pointer operator->() noexcept - { - return &(*m_container)[*m_itr]; - } - general_array_view_iterator& operator++() noexcept - { - ++m_itr; - return *this; - } - general_array_view_iterator operator++(int)noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - general_array_view_iterator& operator--() noexcept - { - --m_itr; - return *this; - } - general_array_view_iterator operator--(int)noexcept - { - auto ret = *this; - --(*this); - return ret; - } - general_array_view_iterator operator+(difference_type n) const noexcept - { - general_array_view_iterator ret{ *this }; - return ret += n; - } - general_array_view_iterator& operator+=(difference_type n) noexcept - { - m_itr += n; - return *this; - } - general_array_view_iterator operator-(difference_type n) const noexcept - { - general_array_view_iterator ret{ *this }; - return ret -= n; - } - general_array_view_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - difference_type operator-(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const noexcept - { - return (*m_container)[m_itr[n]];; - } - bool operator==(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator !=(const general_array_view_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - bool operator<(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_array_view_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const general_array_view_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const general_array_view_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - void swap(general_array_view_iterator& rhs) noexcept - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } + reference operator*() noexcept + { + return (*m_container)[*m_itr]; + } + pointer operator->() noexcept + { + return &(*m_container)[*m_itr]; + } + general_array_view_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_array_view_iterator operator++(int)noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_array_view_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_array_view_iterator operator--(int)noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_array_view_iterator operator+(difference_type n) const noexcept + { + general_array_view_iterator ret{ *this }; + return ret += n; + } + general_array_view_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_array_view_iterator operator-(difference_type n) const noexcept + { + general_array_view_iterator ret{ *this }; + return ret -= n; + } + general_array_view_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + difference_type operator-(const general_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept + { + return (*m_container)[m_itr[n]];; + } + bool operator==(const general_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator !=(const general_array_view_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + bool operator<(const general_array_view_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_array_view_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + bool operator>(const general_array_view_iterator& rhs) const noexcept + { + return rhs < *this; + } + bool operator>=(const general_array_view_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + void swap(general_array_view_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } }; template general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) noexcept { - return rhs + n; + return rhs + n; } } // namespace gsl -- cgit v1.2.3 From dbf0d5017c43beacd3ef885b6ea45b233e3690e5 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 4 Nov 2015 12:17:41 -0800 Subject: Reactivated tests disabled during development. --- tests/array_view_tests.cpp | 140 ++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index d9d0a42..dd2067f 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -241,11 +241,11 @@ SUITE(array_view_tests) auto av8 = av7.as_array_view(); - //CHECK(av8.size() == av6.size()); - //for (auto i = 0; i < av8.size(); i++) - //{ - // CHECK(av8[i] == 1); - //} + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) + { + CHECK(av8[i] == 1); + } #ifdef CONFIRM_COMPILATION_ERRORS struct Foo {char c[11];}; @@ -404,71 +404,71 @@ SUITE(array_view_tests) array_view av2{ av }; CHECK(av2[1] == 5); - // static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); - // - // const strided_array_view src{ arr, {2, 1} }; - // strided_array_view sav{ src }; - // CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); - // CHECK(sav.bounds().stride() == 1); - // CHECK(sav[1] == 5); - // - // static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); - } -// -// // Check copy constructor -// { -// int arr1[2] = { 3, 4 }; -// const strided_array_view src1{ arr1, {2, 1} }; -// strided_array_view 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_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; -// strided_array_view 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_array_view src{ arr1, {{2}, {1}} }; -// strided_array_view sav{ arr2, {{3}, {2}} }; -// strided_array_view& 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_array_view src1{ arr1, {2, 1} }; -// strided_array_view sav1{ arr1b, {1, 1} }; -// strided_array_view& 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_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; -// strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; -// strided_array_view& 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); -// } + static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); + + const strided_array_view src{ arr, {2, 1} }; + strided_array_view sav{ src }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().stride() == 1); + CHECK(sav[1] == 5); + + static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); + } + + // Check copy constructor + { + int arr1[2] = { 3, 4 }; + const strided_array_view src1{ arr1, {2, 1} }; + strided_array_view 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_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; + strided_array_view 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_array_view src{ arr1, {{2}, {1}} }; + strided_array_view sav{ arr2, {{3}, {2}} }; + strided_array_view& 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_array_view src1{ arr1, {2, 1} }; + strided_array_view sav1{ arr1b, {1, 1} }; + strided_array_view& 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_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; + strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; + strided_array_view& 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_array_view_slice) -- cgit v1.2.3 From b63ec949e99963e17f96f131ae5458c437b014bc Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 4 Nov 2015 12:42:27 -0800 Subject: Renaming array_view and string_view, as per WG21 discussions. --- include/array_view.h | 1988 ------------------------------------------- include/gsl.h | 4 +- include/span.h | 1988 +++++++++++++++++++++++++++++++++++++++++++ include/string_span.h | 181 ++++ include/string_view.h | 184 ---- tests/CMakeLists.txt | 4 +- tests/array_view_tests.cpp | 1803 --------------------------------------- tests/bounds_tests.cpp | 4 +- tests/span_tests.cpp | 1803 +++++++++++++++++++++++++++++++++++++++ tests/string_span_tests.cpp | 112 +++ tests/string_view_tests.cpp | 112 --- 11 files changed, 4090 insertions(+), 4093 deletions(-) delete mode 100644 include/array_view.h create mode 100644 include/span.h create mode 100644 include/string_span.h delete mode 100644 include/string_view.h delete mode 100644 tests/array_view_tests.cpp create mode 100644 tests/span_tests.cpp create mode 100644 tests/string_span_tests.cpp delete mode 100644 tests/string_view_tests.cpp diff --git a/include/array_view.h b/include/array_view.h deleted file mode 100644 index cbc33be..0000000 --- a/include/array_view.h +++ /dev/null @@ -1,1988 +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_ARRAY_VIEW_H -#define GSL_ARRAY_VIEW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "fail_fast.h" - -#ifdef _MSC_VER - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr /* nothing */ - - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#pragma push_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG - - -// noexcept is not understood -#ifndef GSL_THROWS_FOR_TESTING -#define noexcept /* nothing */ -#endif - -// 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 - -// 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 { - -/* -** begin definitions of index and bounds -*/ -namespace details -{ - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; - - - template - class are_integral : public std::integral_constant {}; - - template - class are_integral : public std::integral_constant::value && are_integral::value> {}; -} - -template -class index final -{ - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class index; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr index() noexcept - {} - - constexpr index(const value_type(&values)[Rank]) noexcept - { - std::copy(values, values + Rank, elems); - } - -#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) - {} -#else - template::value>> - constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } - {} -#endif - - constexpr index(const index& other) noexcept = default; - - constexpr index& operator=(const index& rhs) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const noexcept - { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); - return elems[component_idx]; - } - - constexpr bool operator==(const index& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const index& rhs) const noexcept - { - return !(this == rhs); - } - - constexpr index operator+() const noexcept - { - return *this; - } - - constexpr index operator-() const noexcept - { - index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr index operator+(const index& rhs) const noexcept - { - index ret = *this; - ret += rhs; - return ret; - } - - constexpr index operator-(const index& rhs) const noexcept - { - index ret = *this; - ret -= rhs; - return ret; - } - - constexpr index& operator+=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr index operator*(value_type v) const noexcept - { - index ret = *this; - ret *= v; - return ret; - } - - constexpr index operator/(value_type v) const noexcept - { - index ret = *this; - ret /= v; - return ret; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return rhs * v; - } - - constexpr index& operator*=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } - -private: - value_type elems[Rank] = {}; -}; - -#ifndef _MSC_VER - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const noexcept - { - return static_cast(-1); - } - - template ::value>> - constexpr bool operator ==(T other) const noexcept - { - return static_cast(-1) == other; - } - - template ::value>> - constexpr bool operator !=(T other) const noexcept - { - return static_cast(-1) != other; - } - -}; - -template ::value>> -constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right == left; -} - -template ::value>> -constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const std::ptrdiff_t dynamic_range = -1; -#endif - -struct generalized_mapping_tag {}; -struct contiguous_mapping_tag : generalized_mapping_tag {}; - -namespace details -{ - - template - struct LessThan - { - static const bool value = Left < Right; - }; - - template - struct BoundsRanges { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; - - // TODO : following signature is for work around VS bug - template - BoundsRanges(const OtherRange&, bool /* firstLevel */) - {} - - BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) { } - BoundsRanges() = default; - - - template - void serialize(T&) const - {} - - template - size_type linearize(const T&) const - { - return 0; - } - - template - bool contains(const T&) const - { - return 0; - } - - size_type totalSize() const noexcept - { - return TotalSize; - } - - bool operator==(const BoundsRanges&) const noexcept - { - return true; - } - }; - - template - struct BoundsRanges : BoundsRanges{ - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges (const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - fail_fast_assert(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : - Base(static_cast&>(other), false), m_bound(other.totalSize()) - {} - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - const size_type index = this->Base::totalSize() * arr[Dim]; - fail_fast_assert(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T & arr) const - { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) - return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const noexcept - { - return m_bound; - } - - size_type elementNum() const noexcept - { - return totalSize() / this->Base::totalSize(); - } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator == (const BoundsRanges & rhs) const noexcept - { - return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) - { - fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); - return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - if (arr[Dim] >= CurrentRange) - return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) - return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const noexcept - { - return CurrentRange * this->Base::totalSize(); - } - - size_type elementNum() const noexcept - { - return CurrentRange; - } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator== (const BoundsRanges& rhs) const noexcept - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible2; - - template > - auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant())) - {}; - - template - struct BoundsRangeConvertible2 : std::true_type {}; - - template - struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) - {}; - template - struct BoundsRangeConvertible : std::true_type {}; - - template - struct TypeListIndexer - { - const TypeChain & obj; - TypeListIndexer(const TypeChain & obj) :obj(obj){} - template - const TypeChain & getObj(std::true_type) - { - return obj; - } - template - auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) - { - return TypeListIndexer(static_cast(obj)).template get(); - } - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain &obj) - { - return TypeListIndexer(obj); - } - - template 1), typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) - { - ret[i] = other[i + 1]; - } - return ret; - } -} - -template -class bounds_iterator; - -template -class static_bounds -{ -public: - static_bounds(const details::BoundsRanges&) { - } -}; - -template -class static_bounds -{ - using MyRanges = details::BoundsRanges; - - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) - {} - - template - friend class static_bounds; - -public: - static const size_t rank = MyRanges::Depth; - static const size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; - - using size_type = std::ptrdiff_t; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; - - constexpr static_bounds(const static_bounds&) = default; - - template , details::BoundsRanges >::value>> - constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - {} - - constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) - { - fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); - fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); - } - - constexpr static_bounds() = default; - - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new(&m_ranges) MyRanges (otherBounds.m_ranges); - return *this; - } - - constexpr sliced_type slice() const noexcept - { - return sliced_type{static_cast &>(m_ranges)}; - } - - constexpr size_type stride() const noexcept - { - return rank > 1 ? slice().size() : 1; - } - - constexpr size_type size() const noexcept - { - return m_ranges.totalSize(); - } - - constexpr size_type total_size() const noexcept - { - return m_ranges.totalSize(); - } - - constexpr size_type linearize(const index_type & idx) const - { - return m_ranges.linearize(idx); - } - - constexpr bool contains(const index_type& idx) const noexcept - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](size_t index) const noexcept - { - return m_ranges.elementNum(index); - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - constexpr index_type index_bounds() const noexcept - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return{ extents }; - } - - template - constexpr bool operator == (const static_bounds& rhs) const noexcept - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator != (const static_bounds& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const noexcept - { - return const_iterator(*this, index_type{}); - } - - constexpr const_iterator end() const noexcept - { - return const_iterator(*this, this->index_bounds()); - } -}; - -template -class strided_bounds -{ - template - friend class strided_bounds; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; - - constexpr strided_bounds(const strided_bounds &) noexcept = default; - - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : m_extents(values), m_strides(std::move(strides)) - {} - - constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept - : m_extents(extents), m_strides(strides) - {} - - constexpr index_type strides() const noexcept - { - return m_strides; - } - - constexpr size_type total_size() const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; ++i) - { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - - constexpr size_type size() const noexcept - { - size_type ret = 1; - for (size_t i = 0; i < rank; ++i) - { - ret *= m_extents[i]; - } - return ret; - } - - constexpr bool contains(const index_type& idx) const noexcept - { - for (size_t i = 0; i < rank; ++i) - { - if (idx[i] < 0 || idx[i] >= m_extents[i]) - return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; i++) - { - fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); - ret += idx[i] * m_strides[i]; - } - return ret; - } - - constexpr size_type stride() const noexcept - { - return m_strides[0]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return{ details::shift_left(m_extents), details::shift_left(m_strides) }; - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - - constexpr index_type index_bounds() const noexcept - { - return m_extents; - } - constexpr const_iterator begin() const noexcept - { - return const_iterator{ *this, index_type{} }; - } - - constexpr const_iterator end() const noexcept - { - return const_iterator{ *this, index_bounds() }; - } - -private: - index_type m_extents; - index_type m_strides; -}; - -template -struct is_bounds : std::integral_constant {}; -template -struct is_bounds> : std::integral_constant {}; -template -struct is_bounds> : std::integral_constant {}; - -template -class bounds_iterator: public std::iterator -{ -private: - using Base = std::iterator ; - -public: - static const size_t rank = IndexType::rank; - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary(bnd.index_bounds()), curr(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const noexcept - { - return curr; - } - - constexpr pointer operator->() const noexcept - { - return &curr; - } - - constexpr bounds_iterator& operator++() noexcept - { - for (size_t i = rank; i-- > 0;) - { - if (curr[i] < boundary[i] - 1) - { - curr[i]++; - return *this; - } - curr[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr = boundary; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - if (!less(curr, boundary)) - { - // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) - { - curr[i] = boundary[i] - 1; - } - return *this; - } - for (size_t i = rank; i-- > 0;) - { - if (curr[i] >= 1) - { - curr[i]--; - return *this; - } - curr[i] = boundary[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - fail_fast_assert(false); - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - auto linear_idx = linearize(curr) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) - { - stride[i] = stride[i + 1] * boundary[i + 1]; - } - for (size_t i = 0; i < rank; ++i) - { - curr[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{ *this }; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return linearize(curr) - linearize(rhs.curr); - } - - constexpr value_type operator[](difference_type n) const noexcept - { - return *(*this + n); - } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr == rhs.curr; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return less(curr, rhs.curr); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(bounds_iterator& rhs) noexcept - { - std::swap(boundary, rhs.boundary); - std::swap(curr, rhs.curr); - } -private: - constexpr bool less(index_type& one, index_type& other) const noexcept - { - for (size_t i = 0; i < rank; ++i) - { - if (one[i] < other[i]) - return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const noexcept - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary)) - { - res = 1; - for (size_t i = rank; i-- > 0;) - { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary[i]; - } - } - else - { - for (size_t i = rank; i-- > 0;) - { - res += idx[i] * multiplier; - multiplier *= boundary[i]; - } - } - return res; - } - - value_type boundary; - std::remove_const_t curr; -}; - -template -bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept -{ - return rhs + n; -} - -// -// begin definitions of basic_array_view -// -namespace details -{ - template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) - { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return{ stride }; - } - - template - void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) - { - static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); - static_assert(std::is_same::value, "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); - fail_fast_assert(src.size() == dest.size()); - } - - -} // namespace details - -template -class contiguous_array_view_iterator; -template -class general_array_view_iterator; -enum class byte : std::uint8_t {}; - -template -class basic_array_view -{ -public: - static const size_t rank = BoundsType::rank; - using bounds_type = BoundsType; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = ValueType*; - using reference = ValueType&; - using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; - using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = std::conditional_t>; - -private: - pointer m_pdata; - bounds_type m_bounds; - -public: - constexpr bounds_type bounds() const noexcept - { - return m_bounds; - } - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_bounds.template extent(); - } - constexpr size_type size() const noexcept - { - return m_bounds.size(); - } - constexpr reference operator[](const index_type& idx) const - { - return m_pdata[m_bounds.linearize(idx)]; - } - constexpr pointer data() const noexcept - { - return m_pdata; - } - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); - const size_type ridx = idx * m_bounds.stride(); - - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); - return Ret {m_pdata + ridx, m_bounds.slice()}; - } - - constexpr operator bool () const noexcept - { - return m_pdata != nullptr; - } - - constexpr iterator begin() const - { - return iterator {this, true}; - } - constexpr iterator end() const - { - return iterator {this, false}; - } - constexpr const_iterator cbegin() const - { - return const_iterator {reinterpret_cast *>(this), true}; - } - constexpr const_iterator cend() const - { - return const_iterator {reinterpret_cast *>(this), false}; - } - - constexpr reverse_iterator rbegin() const - { - return reverse_iterator {end()}; - } - constexpr reverse_iterator rend() const - { - return reverse_iterator {begin()}; - } - constexpr const_reverse_iterator crbegin() const - { - return const_reverse_iterator {cend()}; - } - constexpr const_reverse_iterator crend() const - { - return const_reverse_iterator {cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator== (const basic_array_view & other) const noexcept - { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!= (const basic_array_view & other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator< (const basic_array_view & other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<= (const basic_array_view & other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator> (const basic_array_view & other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>= (const basic_array_view & other) const noexcept - { - return !(*this < other); - } - -public: - template ::value - && std::is_convertible::value>> - constexpr basic_array_view(const basic_array_view & other ) noexcept - : m_pdata(other.m_pdata), m_bounds(other.m_bounds) - { - } -protected: - - constexpr basic_array_view(pointer data, bounds_type bound) noexcept - : m_pdata(data) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept - : m_pdata(reinterpret_cast(data)) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_array_view as_array_view(const DestBounds &bounds) - { - details::verifyBoundsReshape(m_bounds, bounds); - return {m_pdata, bounds}; - } -private: - - friend iterator; - friend const_iterator; - template - friend class basic_array_view; -}; - -template -struct dim -{ - static const std::ptrdiff_t value = DimSize; -}; -template <> -struct dim -{ - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - dim(std::ptrdiff_t size) : dvalue(size) {} -}; - -template -class array_view; - -template -class strided_array_view; - -namespace details -{ - template - struct ArrayViewTypeTraits - { - using value_type = T; - using size_type = size_t; - }; - - template - struct ArrayViewTypeTraits::type> - { - using value_type = typename Traits::array_view_traits::value_type; - using size_type = typename Traits::array_view_traits::size_type; - }; - - template - struct ArrayViewArrayTraits { - using type = array_view; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - fail_fast_assert(totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - fail_fast_assert(BoundsType::static_size == totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl(totalSize, std::integral_constant()); - } - - struct Sep{}; - - template - T static_as_array_view_helper(Sep, Args... args) - { - return T{static_cast(args)...}; - } - template - std::enable_if_t>::value && !std::is_same::value, T> static_as_array_view_helper(Arg, Args... args) - { - return static_as_array_view_helper(args...); - } - template - T static_as_array_view_helper(dim val, Args ... args) - { - return static_as_array_view_helper(args..., val.dvalue); - } - - template - struct static_as_array_view_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_array_view_oracle : std::false_type - {}; - - template - struct is_array_view_oracle> : std::true_type - {}; - - template - struct is_array_view_oracle> : std::true_type - {}; - - template - struct is_array_view : is_array_view_oracle> - {}; - -} - - -template -class array_view : public basic_array_view > -{ - template - friend class array_view; - - using Base = basic_array_view>; - -public: - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - using Base::rank; - -public: - // basic - constexpr array_view(pointer ptr, size_type size) : Base(ptr, bounds_type{ size }) - {} - - constexpr array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) - {} - - constexpr array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) - {} - - constexpr array_view(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) - { - fail_fast_assert(size == 0); - } - - // default - template > - constexpr array_view() : Base(nullptr, bounds_type()) - {} - - // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template - /*typename Dummy = std::enable_if_t::value>*/> - constexpr array_view(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) - {} - - // from n-dimensions static array - template , - typename = std::enable_if_t::value>> - constexpr array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) - {} - - // from n-dimensions static array with size - template , - typename = std::enable_if_t::value> - > - constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{size}) - { - fail_fast_assert(size <= N); - } - - // from std array - template , typename Base::bounds_type>::value> - > - constexpr array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) - {} - - template , typename Base::bounds_type>::value - && std::is_const::value> - > - constexpr array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) - {} - - // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity - template ::value - && details::LessThan::value> - > // remove literal 0 case - constexpr array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) - {} - - // from containers. It must has .size() and .data() two function signatures - template ::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> - > - constexpr array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) - {} - - constexpr array_view(const array_view &) = default; - - // convertible - template >, - typename OtherBaseType = basic_array_view>, - typename Dummy = std::enable_if_t::value> - > - constexpr array_view(const array_view &av) - : Base(static_cast::Base&>(av)) - {} - - // reshape - // DimCount here is a workaround for a bug in MSVC 2015 - template 0)>> - constexpr array_view as_array_view(Dimensions2... dims) - { - using BoundsType = typename array_view::bounds_type; - auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(this->bounds(), tobounds); - return {this->data(), tobounds}; - } - - // to bytes array - template >::value> - auto as_bytes() const noexcept -> array_view - { - static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; - } - - template >::value> - auto as_writeable_bytes() const noexcept -> array_view - { - static_assert(Enabled, "The value_type of array_view must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; - } - - // from bytes array - template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> - { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; - } - - template::value, typename = std::enable_if_t> - constexpr auto as_array_view() const noexcept -> array_view(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> - { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; - } - - // section on linear space - template - constexpr array_view first() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed - return { this->data(), Count }; - } - - constexpr array_view first(size_type count) const noexcept - { - fail_fast_assert(count <= this->size()); - return { this->data(), count }; - } - - template - constexpr array_view last() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); - return { this->data() + this->size() - Count, Count }; - } - - constexpr array_view last(size_type count) const noexcept - { - fail_fast_assert(count <= this->size()); - return { this->data() + this->size() - count, count }; - } - - template - constexpr array_view sub() const noexcept - { - static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); - return { this->data() + Offset, Count }; - } - - constexpr array_view sub(size_type offset, size_type count = dynamic_range) const noexcept - { - fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); - return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; - } - - // size - constexpr size_type length() const noexcept - { - return this->size(); - } - - constexpr size_type used_length() const noexcept - { - return length(); - } - - constexpr size_type bytes() const noexcept - { - return sizeof(value_type) * this->size(); - } - - constexpr size_type used_bytes() const noexcept - { - return bytes(); - } - - // section - constexpr strided_array_view section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; - } - - constexpr reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } - - template 1), typename Dummy = std::enable_if_t> - constexpr array_view operator[](size_type idx) const - { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds() }; - } - - using Base::operator==; - using Base::operator!=; - using Base::operator<; - using Base::operator<=; - using Base::operator>; - using Base::operator>=; -}; - -template -constexpr auto as_array_view(T* const& ptr, dim... args) -> array_view, Dimensions...> -{ - return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; -} - -template -constexpr auto as_array_view (T* arr, std::ptrdiff_t len) -> typename details::ArrayViewArrayTraits::type -{ - return {reinterpret_cast*>(arr), len}; -} - -template -constexpr auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type -{ - return {arr}; -} - -template -constexpr array_view as_array_view(const std::array &arr) -{ - return {arr}; -} - -template -constexpr array_view as_array_view(const std::array &&) = delete; - -template -constexpr array_view as_array_view(std::array &arr) -{ - return {arr}; -} - -template -constexpr array_view as_array_view(T *begin, T *end) -{ - return {begin, end}; -} - -template -constexpr auto as_array_view(Cont &arr) -> std::enable_if_t>::value, - array_view, dynamic_range>> -{ - fail_fast_assert(arr.size() < PTRDIFF_MAX); - return {arr.data(), static_cast(arr.size())}; -} - -template -constexpr auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, - array_view, dynamic_range>> = delete; - -template -class strided_array_view : public basic_array_view> -{ - using Base = basic_array_view>; - - template - friend class strided_array_view; - -public: - using Base::rank; - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - - // from static array of size N - template - strided_array_view(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); - } - - // from raw data - strided_array_view(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); - } - - // from array view - template > - strided_array_view(array_view av, bounds_type bounds) : Base(av.data(), std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); - } - - // convertible - template >, - typename OtherBaseType = basic_array_view>, - typename Dummy = std::enable_if_t::value> - > - constexpr strided_array_view(const strided_array_view &av) : Base(static_cast::Base &>(av)) // static_cast is required - {} - - // convert from bytes - template - strided_array_view::value, OtherValueType>::type, rank> as_strided_array_view() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; - } - - strided_array_view section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return Base::operator[](idx); - } - - template 1), typename Dummy = std::enable_if_t> - constexpr strided_array_view operator[](size_type idx) const - { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; - } - -private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); - - index_type ret = extent; - ret[rank - 1] /= d; - - return ret; - } - - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) - { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - - return strides; - } - - template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); - - for (size_t i = rank - 1; i > 0; --i) - fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); - - index_type ret = strides / d; - ret[rank - 1] = 1; - - return ret; - } -}; - -template -class contiguous_array_view_iterator : public std::iterator -{ - using Base = std::iterator; -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - -private: - template - friend class basic_array_view; - - pointer m_pdata; - const ArrayView * m_validator; - void validateThis() const - { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); - } - contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} -public: - reference operator*() const noexcept - { - validateThis(); - return *m_pdata; - } - pointer operator->() const noexcept - { - validateThis(); - return m_pdata; - } - contiguous_array_view_iterator& operator++() noexcept - { - ++m_pdata; - return *this; - } - contiguous_array_view_iterator operator++(int)noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_array_view_iterator& operator--() noexcept - { - --m_pdata; - return *this; - } - contiguous_array_view_iterator operator--(int)noexcept - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_array_view_iterator operator+(difference_type n) const noexcept - { - contiguous_array_view_iterator ret{ *this }; - return ret += n; - } - contiguous_array_view_iterator& operator+=(difference_type n) noexcept - { - m_pdata += n; - return *this; - } - contiguous_array_view_iterator operator-(difference_type n) const noexcept - { - contiguous_array_view_iterator ret{ *this }; - return ret -= n; - } - contiguous_array_view_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - difference_type operator-(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata - rhs.m_pdata; - } - reference operator[](difference_type n) const noexcept - { - return *(*this + n); - } - bool operator==(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata == rhs.m_pdata; - } - bool operator!=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - bool operator<(const contiguous_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_validator == rhs.m_validator); - return m_pdata < rhs.m_pdata; - } - bool operator<=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const contiguous_array_view_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const contiguous_array_view_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - void swap(contiguous_array_view_iterator& rhs) noexcept - { - std::swap(m_pdata, rhs.m_pdata); - std::swap(m_validator, rhs.m_validator); - } -}; - -template -contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) noexcept -{ - return rhs + n; -} - -template -class general_array_view_iterator : public std::iterator -{ - using Base = std::iterator; -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; -private: - template - friend class basic_array_view; - - const ArrayView * m_container; - typename ArrayView::bounds_type::iterator m_itr; - general_array_view_iterator(const ArrayView *container, bool isbegin) : - m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - {} -public: - reference operator*() noexcept - { - return (*m_container)[*m_itr]; - } - pointer operator->() noexcept - { - return &(*m_container)[*m_itr]; - } - general_array_view_iterator& operator++() noexcept - { - ++m_itr; - return *this; - } - general_array_view_iterator operator++(int)noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - general_array_view_iterator& operator--() noexcept - { - --m_itr; - return *this; - } - general_array_view_iterator operator--(int)noexcept - { - auto ret = *this; - --(*this); - return ret; - } - general_array_view_iterator operator+(difference_type n) const noexcept - { - general_array_view_iterator ret{ *this }; - return ret += n; - } - general_array_view_iterator& operator+=(difference_type n) noexcept - { - m_itr += n; - return *this; - } - general_array_view_iterator operator-(difference_type n) const noexcept - { - general_array_view_iterator ret{ *this }; - return ret -= n; - } - general_array_view_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - difference_type operator-(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const noexcept - { - return (*m_container)[m_itr[n]];; - } - bool operator==(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator !=(const general_array_view_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - bool operator<(const general_array_view_iterator& rhs) const noexcept - { - fail_fast_assert(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_array_view_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const general_array_view_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const general_array_view_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - void swap(general_array_view_iterator& rhs) noexcept - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } -}; - -template -general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) noexcept -{ - return rhs + n; -} - -} // namespace gsl - -#ifdef _MSC_VER - -#undef constexpr -#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 GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#pragma pop_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") - - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#if defined(GSL_THROWS_FOR_TESTING) -#undef noexcept -#endif // GSL_THROWS_FOR_TESTING - - -#endif // GSL_ARRAY_VIEW_H diff --git a/include/gsl.h b/include/gsl.h index ec75723..e20ac69 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -19,8 +19,8 @@ #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 "span.h" // span, strided_span... +#include "string_span.h" // zstring, string_span, zstring_builder... #include #ifdef _MSC_VER diff --git a/include/span.h b/include/span.h new file mode 100644 index 0000000..74da1aa --- /dev/null +++ b/include/span.h @@ -0,0 +1,1988 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fail_fast.h" + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#pragma push_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG + + +// noexcept is not understood +#ifndef GSL_THROWS_FOR_TESTING +#define noexcept /* nothing */ +#endif + +// 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 + +// 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 { + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + + template + class are_integral : public std::integral_constant {}; + + template + class are_integral : public std::integral_constant::value && are_integral::value> {}; +} + +template +class index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class index; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr index() noexcept + {} + + constexpr index(const value_type(&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) + {} +#else + template::value>> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} +#endif + + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept + { + return !(this == rhs); + } + + constexpr index operator+() const noexcept + { + return *this; + } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return static_cast(-1); + } + + template ::value>> + constexpr bool operator ==(T other) const noexcept + { + return static_cast(-1) == other; + } + + template ::value>> + constexpr bool operator !=(T other) const noexcept + { + return static_cast(-1) != other; + } + +}; + +template ::value>> +constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag {}; +struct contiguous_mapping_tag : generalized_mapping_tag {}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + {} + + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) { } + BoundsRanges() = default; + + + template + void serialize(T&) const + {} + + template + size_type linearize(const T&) const + { + return 0; + } + + template + bool contains(const T&) const + { + return 0; + } + + size_type totalSize() const noexcept + { + return TotalSize; + } + + bool operator==(const BoundsRanges&) const noexcept + { + return true; + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges (const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + fail_fast_assert(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : + Base(static_cast&>(other), false), m_bound(other.totalSize()) + {} + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + fail_fast_assert(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T & arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept + { + return m_bound; + } + + size_type elementNum() const noexcept + { + return totalSize() / this->Base::totalSize(); + } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const noexcept + { + return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges ; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges (const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) + { + fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); + return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) + return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) + return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept + { + return CurrentRange * this->Base::totalSize(); + } + + size_type elementNum() const noexcept + { + return CurrentRange; + } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator== (const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible2; + + template > + auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant())) + {}; + + template + struct BoundsRangeConvertible2 : std::true_type {}; + + template + struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) + {}; + template + struct BoundsRangeConvertible : std::true_type {}; + + template + struct TypeListIndexer + { + const TypeChain & obj; + TypeListIndexer(const TypeChain & obj) :obj(obj){} + template + const TypeChain & getObj(std::true_type) + { + return obj; + } + template + auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) + { + return TypeListIndexer(static_cast(obj)).template get(); + } + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain &obj) + { + return TypeListIndexer(obj); + } + + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) + { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) { + } +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) + {} + + template + friend class static_bounds; + +public: + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template , details::BoundsRanges >::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + {} + + constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) + { + fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); + fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new(&m_ranges) MyRanges (otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast &>(m_ranges)}; + } + + constexpr size_type stride() const noexcept + { + return rank > 1 ? slice().size() : 1; + } + + constexpr size_type size() const noexcept + { + return m_ranges.totalSize(); + } + + constexpr size_type total_size() const noexcept + { + return m_ranges.totalSize(); + } + + constexpr size_type linearize(const index_type & idx) const + { + return m_ranges.linearize(idx); + } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return{ extents }; + } + + template + constexpr bool operator == (const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator != (const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept + { + return const_iterator(*this, index_type{}); + } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds &) noexcept = default; + + constexpr strided_bounds(const value_type(&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + {} + + constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept + : m_extents(extents), m_strides(strides) + {} + + constexpr index_type strides() const noexcept + { + return m_strides; + } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) + { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const noexcept + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) + { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const noexcept + { + for (size_t i = 0; i < rank; ++i) + { + if (idx[i] < 0 || idx[i] >= m_extents[i]) + return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) + { + fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const noexcept + { + return m_strides[0]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return{ details::shift_left(m_extents), details::shift_left(m_strides) }; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const noexcept + { + return m_extents; + } + constexpr const_iterator begin() const noexcept + { + return const_iterator{ *this, index_type{} }; + } + + constexpr const_iterator end() const noexcept + { + return const_iterator{ *this, index_bounds() }; + } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; + +template +class bounds_iterator: public std::iterator +{ +private: + using Base = std::iterator ; + +public: + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary(bnd.index_bounds()), curr(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept + { + return curr; + } + + constexpr pointer operator->() const noexcept + { + return &curr; + } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) + { + if (curr[i] < boundary[i] - 1) + { + curr[i]++; + return *this; + } + curr[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr = boundary; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr, boundary)) + { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) + { + curr[i] = boundary[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) + { + if (curr[i] >= 1) + { + curr[i]--; + return *this; + } + curr[i] = boundary[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + fail_fast_assert(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{ *this }; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) + { + stride[i] = stride[i + 1] * boundary[i + 1]; + } + for (size_t i = 0; i < rank; ++i) + { + curr[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{ *this }; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr) - linearize(rhs.curr); + } + + constexpr value_type operator[](difference_type n) const noexcept + { + return *(*this + n); + } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr == rhs.curr; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr, rhs.curr); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept + { + return rhs < *this; + } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary, rhs.boundary); + std::swap(curr, rhs.curr); + } +private: + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) + { + if (one[i] < other[i]) + return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary)) + { + res = 1; + for (size_t i = rank; i-- > 0;) + { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) + { + res += idx[i] * multiplier; + multiplier *= boundary[i]; + } + } + return res; + } + + value_type boundary; + std::remove_const_t curr; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept +{ + return rhs + n; +} + +// +// begin definitions of basic_span +// +namespace details +{ + template + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) + { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return{ stride }; + } + + template + void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) + { + static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); + static_assert(std::is_same::value, "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); + fail_fast_assert(src.size() == dest.size()); + } + + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; +enum class byte : std::uint8_t {}; + +template +class basic_span +{ +public: + static const size_t rank = BoundsType::rank; + using bounds_type = BoundsType; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = ValueType*; + using reference = ValueType&; + using iterator = std::conditional_t::value, contiguous_span_iterator, general_span_iterator>; + using const_iterator = std::conditional_t::value, contiguous_span_iterator>, general_span_iterator>>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; + +private: + pointer m_pdata; + bounds_type m_bounds; + +public: + constexpr bounds_type bounds() const noexcept + { + return m_bounds; + } + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + return m_bounds.template extent(); + } + constexpr size_type size() const noexcept + { + return m_bounds.size(); + } + constexpr reference operator[](const index_type& idx) const + { + return m_pdata[m_bounds.linearize(idx)]; + } + constexpr pointer data() const noexcept + { + return m_pdata; + } + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + const size_type ridx = idx * m_bounds.stride(); + + fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + return Ret {m_pdata + ridx, m_bounds.slice()}; + } + + constexpr operator bool () const noexcept + { + return m_pdata != nullptr; + } + + constexpr iterator begin() const + { + return iterator {this, true}; + } + constexpr iterator end() const + { + return iterator {this, false}; + } + constexpr const_iterator cbegin() const + { + return const_iterator {reinterpret_cast *>(this), true}; + } + constexpr const_iterator cend() const + { + return const_iterator {reinterpret_cast *>(this), false}; + } + + constexpr reverse_iterator rbegin() const + { + return reverse_iterator {end()}; + } + constexpr reverse_iterator rend() const + { + return reverse_iterator {begin()}; + } + constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator {cend()}; + } + constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator {cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator== (const basic_span & other) const noexcept + { + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!= (const basic_span & other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator< (const basic_span & other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<= (const basic_span & other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator> (const basic_span & other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>= (const basic_span & other) const noexcept + { + return !(*this < other); + } + +public: + template ::value + && std::is_convertible::value>> + constexpr basic_span(const basic_span & other ) noexcept + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) + { + } +protected: + + constexpr basic_span(pointer data, bounds_type bound) noexcept + : m_pdata(data) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + constexpr basic_span(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept + : m_pdata(reinterpret_cast(data)) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + constexpr basic_span as_span(const DestBounds &bounds) + { + details::verifyBoundsReshape(m_bounds, bounds); + return {m_pdata, bounds}; + } +private: + + friend iterator; + friend const_iterator; + template + friend class basic_span; +}; + +template +struct dim +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim(std::ptrdiff_t size) : dvalue(size) {} +}; + +template +class span; + +template +class strided_span; + +namespace details +{ + template + struct ArrayViewTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct ArrayViewTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct ArrayViewArrayTraits { + using type = span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + fail_fast_assert(totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + fail_fast_assert(BoundsType::static_size == totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl(totalSize, std::integral_constant()); + } + + struct Sep{}; + + template + T static_as_span_helper(Sep, Args... args) + { + return T{static_cast(args)...}; + } + template + std::enable_if_t>::value && !std::is_same::value, T> static_as_span_helper(Arg, Args... args) + { + return static_as_span_helper(args...); + } + template + T static_as_span_helper(dim val, Args ... args) + { + return static_as_span_helper(args..., val.dvalue); + } + + template + struct static_as_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_span_oracle : std::false_type + {}; + + template + struct is_span_oracle> : std::true_type + {}; + + template + struct is_span_oracle> : std::true_type + {}; + + template + struct is_span : is_span_oracle> + {}; + +} + + +template +class span : public basic_span > +{ + template + friend class span; + + using Base = basic_span>; + +public: + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; + using typename Base::reference; + using Base::rank; + +public: + // basic + constexpr span(pointer ptr, size_type size) : Base(ptr, bounds_type{ size }) + {} + + constexpr span(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + {} + + constexpr span(std::nullptr_t) : Base(nullptr, bounds_type{}) + {} + + constexpr span(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + { + fail_fast_assert(size == 0); + } + + // default + template > + constexpr span() : Base(nullptr, bounds_type()) + {} + + // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) + template + /*typename Dummy = std::enable_if_t::value>*/> + constexpr span(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) + {} + + // from n-dimensions static array + template , + typename = std::enable_if_t::value>> + constexpr span (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + {} + + // from n-dimensions static array with size + template , + typename = std::enable_if_t::value> + > + constexpr span(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{size}) + { + fail_fast_assert(size <= N); + } + + // from std array + template , typename Base::bounds_type>::value> + > + constexpr span (std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} + + template , typename Base::bounds_type>::value + && std::is_const::value> + > + constexpr span (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + {} + + // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity + template ::value + && details::LessThan::value> + > // remove literal 0 case + constexpr span (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + {} + + // from containers. It must has .size() and .data() two function signatures + template ::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + constexpr span (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + {} + + constexpr span(const span &) = default; + + // convertible + template >, + typename OtherBaseType = basic_span>, + typename Dummy = std::enable_if_t::value> + > + constexpr span(const span &av) + : Base(static_cast::Base&>(av)) + {} + + // reshape + // DimCount here is a workaround for a bug in MSVC 2015 + template 0)>> + constexpr span as_span(Dimensions2... dims) + { + using BoundsType = typename span::bounds_type; + auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(this->bounds(), tobounds); + return {this->data(), tobounds}; + } + + // to bytes array + template >::value> + auto as_bytes() const noexcept -> span + { + static_assert(Enabled, "The value_type of span must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + template >::value> + auto as_writeable_bytes() const noexcept -> span + { + static_assert(Enabled, "The value_type of span must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + // from bytes array + template::value, typename = std::enable_if_t> + constexpr auto as_span() const noexcept -> span(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + } + + template::value, typename = std::enable_if_t> + constexpr auto as_span() const noexcept -> span(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + } + + // section on linear space + template + constexpr span first() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + return { this->data(), Count }; + } + + constexpr span first(size_type count) const noexcept + { + fail_fast_assert(count <= this->size()); + return { this->data(), count }; + } + + template + constexpr span last() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + return { this->data() + this->size() - Count, Count }; + } + + constexpr span last(size_type count) const noexcept + { + fail_fast_assert(count <= this->size()); + return { this->data() + this->size() - count, count }; + } + + template + constexpr span sub() const noexcept + { + static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); + return { this->data() + Offset, Count }; + } + + constexpr span sub(size_type offset, size_type count = dynamic_range) const noexcept + { + fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); + return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; + } + + // size + constexpr size_type length() const noexcept + { + return this->size(); + } + + constexpr size_type used_length() const noexcept + { + return length(); + } + + constexpr size_type bytes() const noexcept + { + return sizeof(value_type) * this->size(); + } + + constexpr size_type used_bytes() const noexcept + { + return bytes(); + } + + // section + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + } + + constexpr reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + + template 1), typename Dummy = std::enable_if_t> + constexpr span operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds() }; + } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; + using Base::operator<=; + using Base::operator>; + using Base::operator>=; +}; + +template +constexpr auto as_span(T* const& ptr, dim... args) -> span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), details::static_as_span_helper>(args..., details::Sep{})}; +} + +template +constexpr auto as_span (T* arr, std::ptrdiff_t len) -> typename details::ArrayViewArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_span (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +{ + return {arr}; +} + +template +constexpr span as_span(const std::array &arr) +{ + return {arr}; +} + +template +constexpr span as_span(const std::array &&) = delete; + +template +constexpr span as_span(std::array &arr) +{ + return {arr}; +} + +template +constexpr span as_span(T *begin, T *end) +{ + return {begin, end}; +} + +template +constexpr auto as_span(Cont &arr) -> std::enable_if_t>::value, + span, dynamic_range>> +{ + fail_fast_assert(arr.size() < PTRDIFF_MAX); + return {arr.data(), static_cast(arr.size())}; +} + +template +constexpr auto as_span(Cont &&arr) -> std::enable_if_t>::value, + span, dynamic_range>> = delete; + +template +class strided_span : public basic_span> +{ + using Base = basic_span>; + + template + friend class strided_span; + +public: + using Base::rank; + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using typename Base::iterator; + using typename Base::const_iterator; + using typename Base::reference; + + // from static array of size N + template + strided_span(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); + } + + // from raw data + strided_span(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); + } + + // from array view + template > + strided_span(span av, bounds_type bounds) : Base(av.data(), std::move(bounds)) + { + fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); + } + + // convertible + template >, + typename OtherBaseType = basic_span>, + typename Dummy = std::enable_if_t::value> + > + constexpr strided_span(const strided_span &av) : Base(static_cast::Base &>(av)) // static_cast is required + {} + + // convert from bytes + template + strided_span::value, OtherValueType>::type, rank> as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; + } + + strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return Base::operator[](idx); + } + + template 1), typename Dummy = std::enable_if_t> + constexpr strided_span operator[](size_type idx) const + { + auto ret = Base::operator[](idx); + return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + + index_type ret = extent; + ret[rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + + for (size_t i = rank - 1; i > 0; --i) + fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); + + index_type ret = strides / d; + ret[rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + +private: + template + friend class basic_span; + + pointer m_pdata; + const ArrayView * m_validator; + void validateThis() const + { + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); + } + contiguous_span_iterator (const ArrayView *container, bool isbegin) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} +public: + reference operator*() const noexcept + { + validateThis(); + return *m_pdata; + } + pointer operator->() const noexcept + { + validateThis(); + return m_pdata; + } + contiguous_span_iterator& operator++() noexcept + { + ++m_pdata; + return *this; + } + contiguous_span_iterator operator++(int)noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() noexcept + { + --m_pdata; + return *this; + } + contiguous_span_iterator operator--(int)noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const noexcept + { + contiguous_span_iterator ret{ *this }; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) noexcept + { + m_pdata += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const noexcept + { + contiguous_span_iterator ret{ *this }; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + difference_type operator-(const contiguous_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata - rhs.m_pdata; + } + reference operator[](difference_type n) const noexcept + { + return *(*this + n); + } + bool operator==(const contiguous_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata == rhs.m_pdata; + } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + bool operator<(const contiguous_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata < rhs.m_pdata; + } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + bool operator>(const contiguous_span_iterator& rhs) const noexcept + { + return rhs < *this; + } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + void swap(contiguous_span_iterator& rhs) noexcept + { + std::swap(m_pdata, rhs.m_pdata); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, const contiguous_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +class general_span_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; +private: + template + friend class basic_span; + + const ArrayView * m_container; + typename ArrayView::bounds_type::iterator m_itr; + general_span_iterator(const ArrayView *container, bool isbegin) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + {} +public: + reference operator*() noexcept + { + return (*m_container)[*m_itr]; + } + pointer operator->() noexcept + { + return &(*m_container)[*m_itr]; + } + general_span_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int)noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_span_iterator operator--(int)noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const noexcept + { + general_span_iterator ret{ *this }; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const noexcept + { + general_span_iterator ret{ *this }; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + difference_type operator-(const general_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept + { + return (*m_container)[m_itr[n]];; + } + bool operator==(const general_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator !=(const general_span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + bool operator<(const general_span_iterator& rhs) const noexcept + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + bool operator>(const general_span_iterator& rhs) const noexcept + { + return rhs < *this; + } + bool operator>=(const general_span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + void swap(general_span_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, const general_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +} // namespace gsl + +#ifdef _MSC_VER + +#undef constexpr +#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 GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#pragma pop_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") + + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROWS_FOR_TESTING) +#undef noexcept +#endif // GSL_THROWS_FOR_TESTING + + +#endif // GSL_SPAN_H diff --git a/include/string_span.h b/include/string_span.h new file mode 100644 index 0000000..beccee4 --- /dev/null +++ b/include/string_span.h @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 "span.h" +#include + +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 +using czstring = const char*; + +template +using cwzstring = const wchar_t*; + +template +using zstring = char*; + +template +using wzstring = wchar_t*; + +// +// string_span and relatives +// +// Note that Extent is always single-dimension only +// +template +using basic_string_span = span; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + + +// +// 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 +span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + fail_fast_assert(*cur == Sentinel); + return{ seq, cur - seq }; +} + + +// +// ensure_z - creates a string_span for a czstring or cwzstring. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// +template +inline basic_string_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline basic_string_span ensure_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + fail_fast_assert(sz[len] == 0); + return{ sz, static_cast(len) }; +} + +inline basic_string_span ensure_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; +} + +inline basic_string_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; +} + +inline basic_string_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; +} + +template +basic_string_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } + +template +basic_string_span::type, dynamic_range> ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), cont.length()); +} + +// +// to_string() allow (explicit) conversions from string_span to string +// +template +std::basic_string::type> to_string(basic_string_span view) +{ + return{ view.data(), view.length() }; +} + + +template +class basic_zstring_builder +{ +public: + using string_span_type = basic_string_span; + using value_type = CharT; + using pointer = CharT*; + using size_type = typename string_span_type::size_type; + using iterator = typename string_span_type::iterator; + + basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {} + + template + 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: + string_span_type sv_; +}; + +template +using zstring_builder = basic_zstring_builder; + +template +using wzstring_builder = basic_zstring_builder; +} + +#endif // GSL_STRING_SPAN_H diff --git a/include/string_view.h b/include/string_view.h deleted file mode 100644 index b5f73c5..0000000 --- a/include/string_view.h +++ /dev/null @@ -1,184 +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 - -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 -using czstring = const char*; - -template -using cwzstring = const wchar_t*; - -template -using zstring = char*; - -template -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 -using basic_string_view = array_view; - -template -using string_view = basic_string_view; - -template -using cstring_view = basic_string_view; - -template -using wstring_view = basic_string_view; - -template -using cwstring_view = basic_string_view; - - -// -// 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 -array_view ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIFF_MAX) -{ - auto cur = seq; - while ((cur - seq) < max && *cur != Sentinel) ++cur; - fail_fast_assert(*cur == Sentinel); - return{ seq, 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 -inline basic_string_view ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) -{ - return ensure_sentinel(sz, max); -} - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline basic_string_view ensure_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); - return{ sz, static_cast(len) }; -} - -inline basic_string_view ensure_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; -} - -inline basic_string_view ensure_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; -} - -inline basic_string_view ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; -} - -template -basic_string_view ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } - -template -basic_string_view::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 -std::basic_string::type> to_string(basic_string_view view) -{ - return{ view.data(), view.length() }; -} - - -template -class basic_zstring_builder -{ -public: - using string_view_type = basic_string_view; - 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 - 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 -using zstring_builder = basic_zstring_builder; - -template -using wzstring_builder = basic_zstring_builder; -} - -#endif // GSL_STRING_VIEW_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5e4c395..3435a7f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -43,8 +43,8 @@ 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(string_span_tests) add_gsl_test(at_tests) add_gsl_test(bounds_tests) add_gsl_test(notnull_tests) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp deleted file mode 100644 index dd2067f..0000000 --- a/tests/array_view_tests.cpp +++ /dev/null @@ -1,1803 +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 -#include - -#include -#include -#include -#include - -using namespace std; -using namespace gsl; - -namespace -{ - void use(int&) {} - struct BaseClass {}; - struct DerivedClass : BaseClass {}; -} - -SUITE(array_view_tests) -{ - TEST(basics) - { - auto ptr = as_array_view(new int[10], 10); - fill(ptr.begin(), ptr.end(), 99); - for (int num : ptr) - { - CHECK(num == 99); - } - - delete[] ptr.data(); - - - static_bounds<4, dynamic_range, 2> bounds{ 3 }; - -#ifdef CONFIRM_COMPILATION_ERRORS - array_view av(nullptr, bounds); - av.extent(); - av.extent<2>(); - av[8][4][3]; -#endif - } - - TEST (array_view_convertible) - { -#ifdef CONFIRM_COMPILATION_ERRORS - array_view av1(nullptr, b1); -#endif - - auto f = [&]() { array_view av1(nullptr); }; - CHECK_THROW(f(), fail_fast); - - array_view av1(nullptr); - -#ifdef CONFIRM_COMPILATION_ERRORS - static_bounds b12(b11); - b12 = b11; - b11 = b12; - - array_view av1 = nullptr; - array_view av2(av1); - array_view av2(av1); -#endif - - array_view avd; -#ifdef CONFIRM_COMPILATION_ERRORS - array_view avb = avd; -#endif - array_view avcd = avd; - } - - TEST(boundary_checks) - { - int arr[10][2]; - auto av = as_array_view(arr); - - fill(begin(av), end(av), 0); - - av[2][0] = 1; - av[1][1] = 3; - - // out of bounds - CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[{1, 3}] = 3), fail_fast); - - CHECK_THROW(av[10][2], fail_fast); - CHECK_THROW((av[{10,2}]), fail_fast); - } - - void overloaded_func(array_view exp, int expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - void overloaded_func(array_view exp, char expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - void fixed_func(array_view exp, int expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - TEST(array_view_parameter_test) - { - auto data = new int[4][3][5]; - - auto av = as_array_view(data, 4); - - CHECK(av.size() == 60); - - fill(av.begin(), av.end(), 34); - - int count = 0; - for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); - CHECK(count == 34 * 60); - overloaded_func(av, 34); - - overloaded_func(av.as_array_view(dim<>(4), dim<>(3), dim<>(5)), 34); - - //fixed_func(av, 34); - delete[] data; - } - - - TEST(md_access) - { - auto width = 5, height = 20; - - auto imgSize = width * height; - auto image_ptr = new int[imgSize][3]; - - // size check will be done - auto image_view = as_array_view(image_ptr, imgSize).as_array_view(dim<>(height), dim<>(width), dim<3>()); - - iota(image_view.begin(), image_view.end(), 1); - - int expected = 0; - for (auto i = 0; i < height; i++) - { - for (auto j = 0; j < width; j++) - { - CHECK(expected + 1 == image_view[i][j][0]); - CHECK(expected + 2 == image_view[i][j][1]); - CHECK(expected + 3 == image_view[i][j][2]); - - auto val = image_view[{i, j, 0}]; - CHECK(expected + 1 == val); - val = image_view[{i, j, 1}]; - CHECK(expected + 2 == val); - val = image_view[{i, j, 2}]; - CHECK(expected + 3 == val); - - expected += 3; - } - } - } - - TEST(array_view_factory_test) - { - { - int * arr = new int[150]; - - auto av = as_array_view(arr, dim<10>(), dim<>(3), dim<5>()); - - fill(av.begin(), av.end(), 24); - overloaded_func(av, 24); - - delete[] arr; - - - array stdarr{ 0 }; - auto av2 = as_array_view(stdarr); - overloaded_func(av2.as_array_view(dim<>(1), dim<3>(), dim<5>()), 0); - - - string str = "ttttttttttttttt"; // size = 15 - auto t = str.data(); - auto av3 = as_array_view(str); - overloaded_func(av3.as_array_view(dim<>(1), dim<3>(), dim<5>()), 't'); - } - - { - int a[3][4][5]; - auto av = as_array_view(a); - const int (*b)[4][5]; - b = a; - auto bv = as_array_view(b, 3); - - CHECK(av == bv); - - const std::array arr = {0.0, 0.0, 0.0}; - auto cv = as_array_view(arr); - - vector vec(3); - auto dv = as_array_view(vec); - -#ifdef CONFIRM_COMPILATION_ERRORS - auto dv2 = as_array_view(std::move(vec)); -#endif - } - } - - template void fn(const Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } - TEST (array_view_reshape_test) - { - int a[3][4][5]; - auto av = as_array_view(a); - fn(av.bounds()); - auto av2 = av.as_array_view(dim<60>()); - auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); - auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); - auto av5 = av4.as_array_view(dim<3>(), dim<4>(), dim<5>()); - auto av6 = av5.as_array_view(dim<12>(), dim<>(5)); - - fill(av6.begin(), av6.end(), 1); - - auto av7 = av6.as_bytes(); - - auto av8 = av7.as_array_view(); - - CHECK(av8.size() == av6.size()); - for (auto i = 0; i < av8.size(); i++) - { - CHECK(av8[i] == 1); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - struct Foo {char c[11];}; - auto av9 = av7.as_array_view(); -#endif - } - - - TEST (array_view_section_test) - { - int a[30][4][5]; - - auto av = as_array_view(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}); - } - - TEST(array_view_section) - { - std::vector data(5 * 10); - std::iota(begin(data), end(data), 0); - const array_view av = as_array_view(data).as_array_view(dim<5>(), dim<10>()); - - strided_array_view 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_array_view 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_array_view_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_array_view 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_array_view 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_array_view 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 array_view constructor - { - int arr[] = { 1, 2 }; - - // From non-cv-qualified source - { - const array_view src = arr; - - strided_array_view 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_array_view sav_c{ {src}, {2, 1} }; -#else - strided_array_view sav_c{ array_view{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_array_view sav_v{ {src}, {2, 1} }; -#else - strided_array_view sav_v{ array_view{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_array_view sav_cv{ {src}, {2, 1} }; -#else - strided_array_view sav_cv{ array_view{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 array_view src{ arr }; - - strided_array_view 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_array_view sav_cv{ {src}, {2, 1} }; -#else - strided_array_view sav_cv{ array_view{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 array_view src{ arr }; - - strided_array_view 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_array_view sav_cv{ {src}, {2, 1} }; -#else - strided_array_view sav_cv{ array_view{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 array_view src{ arr }; - - strided_array_view 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 array_view av(arr, 2); - array_view av2{ av }; - CHECK(av2[1] == 5); - - static_assert(std::is_convertible, array_view>::value, "ctor is not implicit!"); - - const strided_array_view src{ arr, {2, 1} }; - strided_array_view sav{ src }; - CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav.bounds().stride() == 1); - CHECK(sav[1] == 5); - - static_assert(std::is_convertible, strided_array_view>::value, "ctor is not implicit!"); - } - - // Check copy constructor - { - int arr1[2] = { 3, 4 }; - const strided_array_view src1{ arr1, {2, 1} }; - strided_array_view 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_array_view src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; - strided_array_view 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_array_view src{ arr1, {{2}, {1}} }; - strided_array_view sav{ arr2, {{3}, {2}} }; - strided_array_view& 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_array_view src1{ arr1, {2, 1} }; - strided_array_view sav1{ arr1b, {1, 1} }; - strided_array_view& 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_array_view src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; - strided_array_view sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; - strided_array_view& 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_array_view_slice) - { - std::vector data(5 * 10); - std::iota(begin(data), end(data), 0); - const array_view src = as_array_view(data).as_array_view(dim<5>(), dim<10>()); - - const strided_array_view sav{ src, {{5, 10}, {10, 1}} }; -#ifdef CONFIRM_COMPILATION_ERRORS - const strided_array_view csav{ {src},{ { 5, 10 },{ 10, 1 } } }; -#endif - const strided_array_view csav{ array_view{ src }, { { 5, 10 },{ 10, 1 } } }; - - strided_array_view sav_sl = sav[2]; - CHECK(sav_sl[0] == 20); - CHECK(sav_sl[9] == 29); - - strided_array_view 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_array_view_column_major) - { - // strided_array_view 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_array_view 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_array_view cm_sl = cm_sav[3]; - - CHECK(cm_sl[0] == 10); - CHECK(cm_sl[1] == 11); - CHECK(cm_sl[2] == 12); - - // Section - strided_array_view 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_array_view_bounds) - { - int arr[] = { 0, 1, 2, 3 }; - array_view 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_array_view sav{ av,{ { 4 },{} } }; - CHECK(sav[0] == 0); - CHECK(sav[3] == 0); - CHECK_THROW(sav[4], fail_fast); - } - - { - // zero extent - strided_array_view sav{ av,{ {},{ 1 } } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // zero extent and stride - strided_array_view sav{ av,{ {},{} } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // strided array ctor with matching strided bounds - strided_array_view 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_array_view 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_array_view 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_array_view { arr, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_array_view { arr, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_array_view { arr, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_array_view { arr, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_array_view { arr, { 5, 5 } }), fail_fast); - } - - { - // bounds cross data boundaries - from array view - CHECK_THROW((strided_array_view { av, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_array_view { av, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_array_view { av, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_array_view { av, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_array_view { av, { 5, 5 } }), fail_fast); - } - - { - // bounds cross data boundaries - from dynamic arrays - CHECK_THROW((strided_array_view { av.data(), 4, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_array_view { av.data(), 4, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_array_view { av.data(), 4, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_array_view { av.data(), 4, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_array_view { av.data(), 4, { 5, 5 } }), fail_fast); - CHECK_THROW((strided_array_view { av.data(), 2, { 2, 2 } }), fail_fast); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - strided_array_view sav0{ av.data(), { 3, 2 } }; - strided_array_view sav1{ arr, { 1 } }; - strided_array_view sav2{ arr, { 1,1,1 } }; - strided_array_view sav3{ av, { 1 } }; - strided_array_view sav4{ av, { 1,1,1 } }; - strided_array_view sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; - strided_array_view sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; - strided_array_view sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; - - index<1> index{ 0, 1 }; - strided_array_view sav8{ arr,{ 1,{ 1,1 } } }; - strided_array_view sav9{ arr,{ { 1,1 },{ 1,1 } } }; - strided_array_view sav10{ av,{ 1,{ 1,1 } } }; - strided_array_view sav11{ av,{ { 1,1 },{ 1,1 } } }; - strided_array_view sav12{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; - strided_array_view sav13{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; - strided_array_view sav14{ av.as_array_view(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; - } -#endif - } - - TEST(strided_array_view_type_conversion) - { - int arr[] = { 0, 1, 2, 3 }; - array_view av(arr); - - { - strided_array_view sav{ av.data(), av.size(), { av.size() / 2, 2 } }; -#ifdef CONFIRM_COMPILATION_ERRORS - strided_array_view lsav1 = sav.as_strided_array_view(); -#endif - } - { - strided_array_view sav{ av, { av.size() / 2, 2 } }; -#ifdef CONFIRM_COMPILATION_ERRORS - strided_array_view lsav1 = sav.as_strided_array_view(); -#endif - } - - array_view bytes = av.as_bytes(); - - // retype strided array with regular strides - from raw data - { - strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - strided_array_view sav2{ bytes.data(), bytes.size(), bounds }; - strided_array_view sav3 = sav2.as_strided_array_view(); - 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 array_view - { - strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); - strided_array_view sav2{ bytes2, bounds }; - strided_array_view sav3 = sav2.as_strided_array_view(); - 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 } }; - array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); - strided_array_view sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_array_view(), fail_fast); - } - - // retype strided array with not enough elements - strides are too small - { - strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; - array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); - strided_array_view sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_array_view(), 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 } }; - array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); - strided_array_view sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_array_view(), 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 } }; - array_view bytes2 = bytes.as_array_view(dim<2>(), dim<>(bytes.size() / 2)); - strided_array_view sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_array_view(), fail_fast); - } - - // retype strided array with irregular strides - from raw data - { - strided_bounds<1> bounds{ bytes.size() / 2, 2 }; - strided_array_view sav2{ bytes.data(), bytes.size(), bounds }; - CHECK_THROW(sav2.as_strided_array_view(), fail_fast); - } - - // retype strided array with irregular strides - from array_view - { - strided_bounds<1> bounds{ bytes.size() / 2, 2 }; - strided_array_view sav2{ bytes, bounds }; - CHECK_THROW(sav2.as_strided_array_view(), fail_fast); - } - } - - TEST(empty_arrays) - { -#ifdef CONFIRM_COMPILATION_ERRORS - { - array_view empty; - strided_array_view empty2; - strided_array_view empty3{ nullptr,{ 0, 1 } }; - } -#endif - - { - array_view empty_av(nullptr); - - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) - { - CHECK(false); - } - } - - { - array_view empty_av = {}; - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) - { - CHECK(false); - } - } - - { - array_view empty_av(nullptr); - strided_array_view 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) - { - CHECK(false); - } - } - - { - strided_array_view 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) - { - CHECK(false); - } - } - } - - TEST(index_constructor) - { - auto arr = new int[8]; - for (int i = 0; i < 4; ++i) - { - arr[2 * i] = 4 + i; - arr[2 * i + 1] = i; - } - - array_view av(arr, 8); - - ptrdiff_t a[1] = { 0 }; - index<1> i = a; - - CHECK(av[i] == 4); - - auto av2 = av.as_array_view(dim<4>(), dim<>(2)); - ptrdiff_t a2[2] = { 0, 1 }; - index<2> i2 = a2; - - CHECK(av2[i2] == 0); - CHECK(av2[0][i] == 4); - - delete[] arr; - } - - TEST(index_constructors) - { - { - // components of the same type - index<3> i1(0, 1, 2); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - size_t c1 = 1; - index<3> i2(c0, c1, 2); - CHECK(i2[0] == 0); - - // from array - index<3> i3 = { 0,1,2 }; - CHECK(i3[0] == 0); - - // from other index of the same size type - index<3> i4 = i3; - CHECK(i4[0] == 0); - - // default - index<3> i7; - CHECK(i7[0] == 0); - - // default - index<3> i9 = {}; - CHECK(i9[0] == 0); - } - - { - // components of the same type - index<1> i1(0); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - index<1> i2(c0); - CHECK(i2[0] == 0); - - // from array - index<1> i3 = { 0 }; - CHECK(i3[0] == 0); - - // from int - index<1> i4 = 0; - CHECK(i4[0] == 0); - - // from other index of the same size type - index<1> i5 = i3; - CHECK(i5[0] == 0); - - // default - index<1> i8; - CHECK(i8[0] == 0); - - // default - index<1> i9 = {}; - CHECK(i9[0] == 0); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - index<3> i1(0, 1); - index<3> i2(0, 1, 2, 3); - index<3> i3 = { 0 }; - index<3> i4 = { 0, 1, 2, 3 }; - index<1> i5 = { 0,1 }; - } -#endif - } - - TEST(index_operations) - { - ptrdiff_t a[3] = { 0, 1, 2 }; - ptrdiff_t b[3] = { 3, 4, 5 }; - index<3> i = a; - index<3> j = b; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - - { - index<3> k = i + j; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 3); - CHECK(k[1] == 5); - CHECK(k[2] == 7); - } - - { - index<3> k = i * 3; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<3> k = 3 * i; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<2> k = details::shift_left(i); - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 1); - CHECK(k[1] == 2); - } - - } - - void iterate_second_column(array_view av) - { - auto length = av.size() / 2; - - // view to the second column - auto section = av.section({ 0,1 }, { length,1 }); - - CHECK(section.size() == length); - for (auto i = 0; i < section.size(); ++i) - { - CHECK(section[i][0] == av[i][1]); - } - - for (auto i = 0; i < section.size(); ++i) - { - auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - - CHECK(section.bounds().index_bounds()[0] == length); - CHECK(section.bounds().index_bounds()[1] == 1); - for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) - { - for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) - { - auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - } - - size_t check_sum = 0; - for (auto i = 0; i < length; ++i) - { - check_sum += av[i][1]; - } - - { - auto idx = 0; - size_t sum = 0; - for (auto num : section) - { - CHECK(num == av[idx][1]); - sum += num; - idx++; - } - - CHECK(sum == check_sum); - } - { - size_t idx = length - 1; - size_t sum = 0; - for (auto iter = section.rbegin(); iter != section.rend(); ++iter) - { - CHECK(*iter == av[idx][1]); - sum += *iter; - idx--; - } - - CHECK(sum == check_sum); - } - } - - TEST(array_view_section_iteration) - { - int arr[4][2] = { { 4,0 },{ 5,1 },{ 6,2 },{ 7,3 } }; - - // static bounds - { - array_view av = arr; - iterate_second_column(av); - } - // first bound is dynamic - { - array_view av = arr; - iterate_second_column(av); - } - // second bound is dynamic - { - array_view av = arr; - iterate_second_column(av); - } - // both bounds are dynamic - { - array_view av = arr; - iterate_second_column(av); - } - } - - TEST(dynamic_array_view_section_iteration) - { - auto height = 4, width = 2; - auto size = height * width; - - auto arr = new int[size]; - for (auto i = 0; i < size; ++i) - { - arr[i] = i; - } - - auto av = as_array_view(arr, size); - - // first bound is dynamic - { - array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // second bound is dynamic - { - array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // both bounds are dynamic - { - array_view av2 = av.as_array_view(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - - delete[] arr; - } - - void iterate_every_other_element(array_view 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_array_view 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_array_view_section_iteration) - { - int arr[8] = {4,0,5,1,6,2,7,3}; - - // static bounds - { - array_view av(arr, 8); - iterate_every_other_element(av); - } - - // dynamic bounds - { - array_view av(arr, 8); - iterate_every_other_element(av); - } - } - - TEST(dynamic_strided_array_view_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_array_view(arr, 8); - iterate_every_other_element(av); - - delete[] arr; - } - - void iterate_second_slice(array_view 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_array_view_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; - } - - { - array_view av = arr; - iterate_second_slice(av); - } - } - - TEST(dynamic_strided_array_view_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_array_view(arr, 24).as_array_view(dim<3>(),dim<4>(),dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_array_view(arr, 24).as_array_view(dim<>(3), dim<4>(), dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_array_view(arr, 24).as_array_view(dim<3>(), dim<>(4), dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_array_view(arr, 24).as_array_view(dim<3>(), dim<4>(), dim<>(2)); - iterate_second_slice(av); - } - delete[] arr; - } - - TEST(strided_array_view_conversion) - { - // get an array_view 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_array_view(arr, 4).as_bytes().as_array_view(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_array_view(); // { { 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_array_view transposed{ cs.data(), cs.bounds().total_size(), reverse_bounds }; - - // slice to get a one-dimensional array of c's - strided_array_view 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++; - } - - } - - TEST(constructors) - { - array_view av(nullptr); - CHECK(av.length() == 0); - - array_view av2; - CHECK(av2.length() == 0); - - array_view av3(nullptr, 0); - CHECK(av3.length() == 0); - - // Constructing from a nullptr + length is specifically disallowed - auto f = [&]() {array_view av4(nullptr, 2);}; - CHECK_THROW(f(), fail_fast); - - int arr1[2][3]; - array_view av5(arr1); - - array arr2; - array_view av6(arr2); - - vector vec1(19); - array_view av7(vec1); - CHECK(av7.length() == 19); - - - array_view av8; - CHECK(av8.length() == 0); - array_view av9(arr2); - CHECK(av9.length() == 15); - - -#ifdef CONFIRM_COMPILATION_ERRORS - array_view av10; - DerivedClass *p = nullptr; - array_view av11(p, 0); -#endif - } - - TEST(copyandassignment) - { - array_view av1; - - int arr[] = {3, 4, 5}; - av1 = arr; - array_view av2; - av2 = av1; - } - - TEST(array_view_first) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - array_view av = arr; - CHECK((av.first<2>().bounds() == static_bounds<2>())); - CHECK(av.first<2>().length() == 2); - CHECK(av.first(2).length() == 2); - } - - { - array_view av = arr; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - - { - array_view av = arr; - CHECK((av.first<5>().bounds() == static_bounds<5>())); - CHECK(av.first<5>().length() == 5); - CHECK(av.first(5).length() == 5); - } - - { - array_view av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK(av.first<6>().bounds() == static_bounds<6>()); - CHECK(av.first<6>().length() == 6); -#endif - CHECK_THROW(av.first(6).length(), fail_fast); - } - - { - array_view av; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - } - - TEST(array_view_last) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - array_view av = arr; - CHECK((av.last<2>().bounds() == static_bounds<2>())); - CHECK(av.last<2>().length() == 2); - CHECK(av.last(2).length() == 2); - } - - { - array_view av = arr; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - - { - array_view av = arr; - CHECK((av.last<5>().bounds() == static_bounds<5>())); - CHECK(av.last<5>().length() == 5); - CHECK(av.last(5).length() == 5); - } - - - { - array_view av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK((av.last<6>().bounds() == static_bounds<6>())); - CHECK(av.last<6>().length() == 6); -#endif - CHECK_THROW(av.last(6).length(), fail_fast); - } - - { - array_view av; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - } - - TEST(custmized_array_view_size) - { - double (*arr)[3][4] = new double[100][3][4]; - array_view av1(arr, 10); - - struct EffectiveStructure - { - double* v1; - ptrdiff_t v2; - }; - CHECK(sizeof(av1) == sizeof(EffectiveStructure)); - - CHECK_THROW(av1[10][3][4], fail_fast); - - array_view av2 = av1.as_array_view(dim<>(5), dim<6>(), dim<4>()); - - } - - TEST(array_view_sub) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - array_view av = arr; - CHECK((av.sub<2,2>().bounds() == static_bounds<2>())); - CHECK((av.sub<2,2>().length() == 2)); - CHECK(av.sub(2,2).length() == 2); - CHECK(av.sub(2,3).length() == 3); - } - - - { - array_view av = arr; - CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<0,0>().length() == 0)); - CHECK(av.sub(0,0).length() == 0); - } - - { - array_view av = arr; - CHECK((av.sub<0,5>().bounds() == static_bounds<5>())); - CHECK((av.sub<0,5>().length() == 5)); - CHECK(av.sub(0,5).length() == 5); - CHECK_THROW(av.sub(0,6).length(), fail_fast); - CHECK_THROW(av.sub(1,5).length(), fail_fast); - } - - { - array_view av = arr; - CHECK((av.sub<5,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<5, 0>().length() == 0)); - CHECK(av.sub(5,0).length() == 0); - CHECK_THROW(av.sub(6,0).length(), fail_fast); - } - - { - array_view av; - CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<0,0>().length() == 0)); - CHECK(av.sub(0,0).length() == 0); - CHECK_THROW((av.sub<1,0>().length()), fail_fast); - } - - { - array_view av; - CHECK(av.sub(0).length() == 0); - CHECK_THROW(av.sub(1).length(), fail_fast); - } - - { - array_view av = arr; - CHECK(av.sub(0).length() == 5); - CHECK(av.sub(1).length() == 4); - CHECK(av.sub(4).length() == 1); - CHECK(av.sub(5).length() == 0); - CHECK_THROW(av.sub(6).length(), fail_fast); - auto av2 = av.sub(1); - for (int i = 0; i < 4; ++i) - CHECK(av2[i] == i+2); - } - - { - array_view av = arr; - CHECK(av.sub(0).length() == 5); - CHECK(av.sub(1).length() == 4); - CHECK(av.sub(4).length() == 1); - CHECK(av.sub(5).length() == 0); - CHECK_THROW(av.sub(6).length(), fail_fast); - auto av2 = av.sub(1); - for (int i = 0; i < 4; ++i) - CHECK(av2[i] == i+2); - } - } - - void AssertNullEmptyProperties(array_view& av) - { - CHECK(av.length() == 0); - CHECK(av.data() == nullptr); - CHECK(!av); - } - - template - void AssertContentsMatch(T a1, U a2) - { - CHECK(a1.length() == a2.length()); - for (auto i = 0; i < a1.length(); ++i) - CHECK(a1[i] == a2[i]); - } - - TEST(TestNullConstruction) - { - array_view av; - AssertNullEmptyProperties(av); - - array_view av2(nullptr); - AssertNullEmptyProperties(av2); - } - - TEST(ArrayConstruction) - { - int a[] = { 1, 2, 3, 4 }; - - array_view av = { &a[1], 3 }; - CHECK(av.length() == 3); - - array_view av3 = { a, 2 }; - CHECK(av3.length() == 2); - - array_view av2 = a; - CHECK(av2.length() == 4); - } - - TEST(NonConstConstConversions) - { - int a[] = { 1, 2, 3, 4 }; - -#ifdef CONFIRM_COMPILATION_ERRORS - array_view cav = a; - array_view av = cav; -#else - array_view av = a; - array_view cav = av; -#endif - AssertContentsMatch(av, cav); - } - - TEST(FixedSizeConversions) - { - int arr[] = { 1, 2, 3, 4 }; - - // converting to an array_view from an equal size array is ok - array_view av4 = arr; - CHECK(av4.length() == 4); - - // converting to dynamic_range a_v is always ok - { - array_view av = av4; - } - { - array_view av = arr; - } - - // initialization or assignment to static array_view that REDUCES size is NOT ok -#ifdef CONFIRM_COMPILATION_ERRORS - { - array_view av2 = arr; - } - { - array_view av2 = av4; - } -#endif - - { - array_view av = arr; - array_view av2 = av; - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - array_view av = arr; - array_view av2 = av.as_array_view(dim<2>(), dim<2>()); - } -#endif - - { - array_view av = arr; - auto f = [&]() {array_view av2 = av.as_array_view(dim<>(2), dim<>(2));}; - CHECK_THROW(f(), fail_fast); - } - - // but doing so explicitly is ok - - // you can convert statically - { - array_view av2 = {arr, 2}; - } - { - array_view av2 = av4.first<1>(); - } - - // ...or dynamically - { - // NB: implicit conversion to array_view from array_view - array_view av2 = av4.first(1); - } - - // initialization or assignment to static array_view that requires size INCREASE is not ok. - int arr2[2] = { 1, 2 }; - -#ifdef CONFIRM_COMPILATION_ERRORS - { - array_view av4 = arr2; - } - { - array_view av2 = arr2; - array_view av4 = av2; - } -#endif - { - auto f = [&]() {array_view av4 = {arr2, 2};}; - CHECK_THROW(f(), fail_fast); - } - - // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one - array_view av = arr2; - auto f = [&](){ array_view av2 = av; }; - CHECK_THROW(f(), fail_fast); - } - - TEST(AsWriteableBytes) - { - int a[] = { 1, 2, 3, 4 }; - - { -#ifdef CONFIRM_COMPILATION_ERRORS - // you should not be able to get writeable bytes for const objects - array_view av = a; - auto wav = av.as_writeable_bytes(); -#endif - } - - { - array_view av; - auto wav = av.as_writeable_bytes(); - CHECK(wav.length() == av.length()); - CHECK(wav.length() == 0); - CHECK(wav.bytes() == 0); - } - - { - array_view av = a; - auto wav = av.as_writeable_bytes(); - CHECK(wav.data() == (byte*)&a[0]); - CHECK(wav.length() == sizeof(a)); - } - } - - TEST(NonConstIterator) - { - int a[] = { 1, 2, 3, 4 }; - - { - array_view av = a; - auto wav = av.as_writeable_bytes(); - for (auto& b : wav) - { - b = byte(0); - } - for (size_t i = 0; i < 4; ++i) - { - CHECK(a[i] == 0); - } - } - - { - array_view av = a; - for (auto& n : av) - { - n = 1; - } - for (size_t i = 0; i < 4; ++i) - { - CHECK(a[i] == 1); - } - } - } - - TEST(ArrayViewComparison) - { - { - int arr[10][2]; - auto av1 = as_array_view(arr); - array_view av2 = av1; - - CHECK(av1 == av2); - - array_view av3 = av1.as_array_view(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - } - - { - auto av1 = nullptr; - auto av2 = nullptr; - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 2, 1 }; // bigger - - array_view av1 = nullptr; - array_view av2 = arr; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; - int arr2[] = { 1, 2 }; - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 1, 2, 3 }; - - array_view av1 = { &arr[0], 2 }; // shorter - array_view av2 = arr; // longer - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; // smaller - int arr2[] = { 2, 1 }; // bigger - - array_view av1 = arr1; - array_view av2 = arr2; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - } -} - -int main(int, const char *[]) -{ - return UnitTest::RunAllTests(); -} diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index aacf3d8..53c44d1 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace std; @@ -55,7 +55,7 @@ SUITE(bounds_test) auto itr = bounds.begin(); #ifdef CONFIRM_COMPILATION_ERRORS - array_view av(nullptr, bounds); + span av(nullptr, bounds); auto itr2 = av.cbegin(); diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp new file mode 100644 index 0000000..4c21116 --- /dev/null +++ b/tests/span_tests.cpp @@ -0,0 +1,1803 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ + void use(int&) {} + struct BaseClass {}; + struct DerivedClass : BaseClass {}; +} + +SUITE(span_tests) +{ + TEST(basics) + { + auto ptr = as_span(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) + { + CHECK(num == 99); + } + + delete[] ptr.data(); + + + static_bounds<4, dynamic_range, 2> bounds{ 3 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + span av(nullptr, bounds); + av.extent(); + av.extent<2>(); + av[8][4][3]; +#endif + } + + TEST (span_convertible) + { +#ifdef CONFIRM_COMPILATION_ERRORS + span av1(nullptr, b1); +#endif + + auto f = [&]() { span av1(nullptr); }; + CHECK_THROW(f(), fail_fast); + + span av1(nullptr); + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + span av1 = nullptr; + span av2(av1); + span av2(av1); +#endif + + span avd; +#ifdef CONFIRM_COMPILATION_ERRORS + span avb = avd; +#endif + span avcd = avd; + } + + TEST(boundary_checks) + { + int arr[10][2]; + auto av = as_span(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10,2}]), fail_fast); + } + + void overloaded_func(span exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void overloaded_func(span exp, char expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void fixed_func(span exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + TEST(span_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_span(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(av.as_span(dim<>(4), dim<>(3), dim<>(5)), 34); + + //fixed_func(av, 34); + delete[] data; + } + + + TEST(md_access) + { + auto width = 5, height = 20; + + auto imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = as_span(image_ptr, imgSize).as_span(dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (auto i = 0; i < height; i++) + { + for (auto j = 0; j < width; j++) + { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(span_factory_test) + { + { + int * arr = new int[150]; + + auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + + array stdarr{ 0 }; + auto av2 = as_span(stdarr); + overloaded_func(av2.as_span(dim<>(1), dim<3>(), dim<5>()), 0); + + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + auto av3 = as_span(str); + overloaded_func(av3.as_span(dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + int a[3][4][5]; + auto av = as_span(a); + const int (*b)[4][5]; + b = a; + auto bv = as_span(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_span(arr); + + vector vec(3); + auto dv = as_span(vec); + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_span(std::move(vec)); +#endif + } + } + + template void fn(const Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } + TEST (span_reshape_test) + { + int a[3][4][5]; + auto av = as_span(a); + fn(av.bounds()); + auto av2 = av.as_span(dim<60>()); + auto av3 = av2.as_span(dim<3>(), dim<4>(), dim<5>()); + auto av4 = av3.as_span(dim<4>(), dim<>(3), dim<5>()); + auto av5 = av4.as_span(dim<3>(), dim<4>(), dim<5>()); + auto av6 = av5.as_span(dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = av6.as_bytes(); + + auto av8 = av7.as_span(); + + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) + { + CHECK(av8[i] == 1); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + struct Foo {char c[11];}; + auto av9 = av7.as_span(); +#endif + } + + + 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}); + } + + TEST(span_section) + { + std::vector data(5 * 10); + std::iota(begin(data), end(data), 0); + const span av = as_span(data).as_span(dim<5>(), dim<10>()); + + strided_span 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 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 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 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 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 src = arr; + + strided_span 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 sav_c{ {src}, {2, 1} }; +#else + strided_span sav_c{ span{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 sav_v{ {src}, {2, 1} }; +#else + strided_span sav_v{ span{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 sav_cv{ {src}, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 sav_cv{ {src}, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 sav_cv{ {src}, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 av(arr, 2); + span av2{ av }; + CHECK(av2[1] == 5); + + static_assert(std::is_convertible, span>::value, "ctor is not implicit!"); + + const strided_span src{ arr, {2, 1} }; + strided_span sav{ src }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().stride() == 1); + CHECK(sav[1] == 5); + + static_assert(std::is_convertible, strided_span>::value, "ctor is not implicit!"); + } + + // Check copy constructor + { + int arr1[2] = { 3, 4 }; + const strided_span src1{ arr1, {2, 1} }; + strided_span 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 src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; + strided_span 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 src{ arr1, {{2}, {1}} }; + strided_span sav{ arr2, {{3}, {2}} }; + strided_span& 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 src1{ arr1, {2, 1} }; + strided_span sav1{ arr1b, {1, 1} }; + strided_span& 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 src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; + strided_span sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; + strided_span& 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 data(5 * 10); + std::iota(begin(data), end(data), 0); + const span src = as_span(data).as_span(dim<5>(), dim<10>()); + + const strided_span sav{ src, {{5, 10}, {10, 1}} }; +#ifdef CONFIRM_COMPILATION_ERRORS + const strided_span csav{ {src},{ { 5, 10 },{ 10, 1 } } }; +#endif + const strided_span csav{ span{ src }, { { 5, 10 },{ 10, 1 } } }; + + strided_span sav_sl = sav[2]; + CHECK(sav_sl[0] == 20); + CHECK(sav_sl[9] == 29); + + strided_span 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 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 cm_sl = cm_sav[3]; + + CHECK(cm_sl[0] == 10); + CHECK(cm_sl[1] == 11); + CHECK(cm_sl[2] == 12); + + // Section + strided_span 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 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 sav{ av,{ { 4 },{} } }; + CHECK(sav[0] == 0); + CHECK(sav[3] == 0); + CHECK_THROW(sav[4], fail_fast); + } + + { + // zero extent + strided_span sav{ av,{ {},{ 1 } } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // zero extent and stride + strided_span sav{ av,{ {},{} } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // strided array ctor with matching strided bounds + strided_span 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 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 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 { arr, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from array view + CHECK_THROW((strided_span { av, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from dynamic arrays + CHECK_THROW((strided_span { av.data(), 4, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 2, { 2, 2 } }), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + strided_span sav0{ av.data(), { 3, 2 } }; + strided_span sav1{ arr, { 1 } }; + strided_span sav2{ arr, { 1,1,1 } }; + strided_span sav3{ av, { 1 } }; + strided_span sav4{ av, { 1,1,1 } }; + strided_span sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } }; + strided_span sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } }; + strided_span sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + + index<1> index{ 0, 1 }; + strided_span sav8{ arr,{ 1,{ 1,1 } } }; + strided_span sav9{ arr,{ { 1,1 },{ 1,1 } } }; + strided_span sav10{ av,{ 1,{ 1,1 } } }; + strided_span sav11{ av,{ { 1,1 },{ 1,1 } } }; + strided_span sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_span sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_span 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 av(arr); + + { + strided_span sav{ av.data(), av.size(), { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + { + strided_span sav{ av, { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + + span bytes = av.as_bytes(); + + // retype strided array with regular strides - from raw data + { + strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; + strided_span sav2{ bytes.data(), bytes.size(), bounds }; + strided_span sav3 = sav2.as_strided_span(); + 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + strided_span sav3 = sav2.as_strided_span(); + 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with not enough elements - strides are too small + { + strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; + span bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from raw data + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes.data(), bytes.size(), bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from span + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + } + + TEST(empty_arrays) + { +#ifdef CONFIRM_COMPILATION_ERRORS + { + span empty; + strided_span empty2; + strided_span empty3{ nullptr,{ 0, 1 } }; + } +#endif + + { + span empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) + { + CHECK(false); + } + } + + { + span empty_av = {}; + CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) + { + CHECK(false); + } + } + + { + span empty_av(nullptr); + strided_span 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) + { + CHECK(false); + } + } + + { + strided_span 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) + { + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) + { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + span av(arr, 8); + + ptrdiff_t a[1] = { 0 }; + index<1> i = a; + + CHECK(av[i] == 4); + + auto av2 = av.as_span(dim<4>(), dim<>(2)); + ptrdiff_t a2[2] = { 0, 1 }; + index<2> i2 = a2; + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = { 0,1,2 }; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = { 0 }; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = { 0 }; + index<3> i4 = { 0, 1, 2, 3 }; + index<1> i5 = { 0,1 }; + } +#endif + } + + TEST(index_operations) + { + ptrdiff_t a[3] = { 0, 1, 2 }; + ptrdiff_t b[3] = { 3, 4, 5 }; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + + } + + void iterate_second_column(span av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({ 0,1 }, { length,1 }); + + CHECK(section.size() == length); + for (auto i = 0; i < section.size(); ++i) + { + CHECK(section[i][0] == av[i][1]); + } + + for (auto i = 0; i < section.size(); ++i) + { + auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) + { + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) + { + auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + } + + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) + { + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) + { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) + { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); + } + } + + TEST(span_section_iteration) + { + int arr[4][2] = { { 4,0 },{ 5,1 },{ 6,2 },{ 7,3 } }; + + // static bounds + { + span av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + span av = arr; + iterate_second_column(av); + } + } + + TEST(dynamic_span_section_iteration) + { + auto height = 4, 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(arr, size); + + // first bound is dynamic + { + span av2 = av.as_span(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + span av2 = av.as_span(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + span av2 = av.as_span(dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + void iterate_every_other_element(span 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 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 av(arr, 8); + iterate_every_other_element(av); + } + + // dynamic bounds + { + span 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 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 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(arr, 24).as_span(dim<3>(),dim<4>(),dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(arr, 24).as_span(dim<>(3), dim<4>(), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(arr, 24).as_span(dim<3>(), dim<>(4), dim<2>()); + iterate_second_slice(av); + } + + { + auto av = as_span(arr, 24).as_span(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(arr, 4).as_bytes().as_span(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(); // { { 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 transposed{ cs.data(), cs.bounds().total_size(), reverse_bounds }; + + // slice to get a one-dimensional array of c's + strided_span 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++; + } + + } + + TEST(constructors) + { + span av(nullptr); + CHECK(av.length() == 0); + + span av2; + CHECK(av2.length() == 0); + + span av3(nullptr, 0); + CHECK(av3.length() == 0); + + // Constructing from a nullptr + length is specifically disallowed + auto f = [&]() {span av4(nullptr, 2);}; + CHECK_THROW(f(), fail_fast); + + int arr1[2][3]; + span av5(arr1); + + array arr2; + span av6(arr2); + + vector vec1(19); + span av7(vec1); + CHECK(av7.length() == 19); + + + span av8; + CHECK(av8.length() == 0); + span av9(arr2); + CHECK(av9.length() == 15); + + +#ifdef CONFIRM_COMPILATION_ERRORS + span av10; + DerivedClass *p = nullptr; + span av11(p, 0); +#endif + } + + TEST(copyandassignment) + { + span av1; + + int arr[] = {3, 4, 5}; + av1 = arr; + span av2; + av2 = av1; + } + + TEST(span_first) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + span av = arr; + CHECK((av.first<2>().bounds() == static_bounds<2>())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + span av = arr; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + span av = arr; + CHECK((av.first<5>().bounds() == static_bounds<5>())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds<6>()); + CHECK(av.first<6>().length() == 6); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + span av; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(span_last) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + span av = arr; + CHECK((av.last<2>().bounds() == static_bounds<2>())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + span av = arr; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + span av = arr; + CHECK((av.last<5>().bounds() == static_bounds<5>())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds<6>())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + span av; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(custmized_span_size) + { + double (*arr)[3][4] = new double[100][3][4]; + span av1(arr, 10); + + struct EffectiveStructure + { + double* v1; + ptrdiff_t v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + span av2 = av1.as_span(dim<>(5), dim<6>(), dim<4>()); + + } + + TEST(span_sub) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + span av = arr; + CHECK((av.sub<2,2>().bounds() == static_bounds<2>())); + CHECK((av.sub<2,2>().length() == 2)); + CHECK(av.sub(2,2).length() == 2); + CHECK(av.sub(2,3).length() == 3); + } + + + { + span av = arr; + CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + } + + { + span av = arr; + CHECK((av.sub<0,5>().bounds() == static_bounds<5>())); + CHECK((av.sub<0,5>().length() == 5)); + CHECK(av.sub(0,5).length() == 5); + CHECK_THROW(av.sub(0,6).length(), fail_fast); + CHECK_THROW(av.sub(1,5).length(), fail_fast); + } + + { + span av = arr; + CHECK((av.sub<5,0>().bounds() == static_bounds<0>())); + CHECK((av.sub<5, 0>().length() == 0)); + CHECK(av.sub(5,0).length() == 0); + CHECK_THROW(av.sub(6,0).length(), fail_fast); + } + + { + span av; + CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + CHECK_THROW((av.sub<1,0>().length()), fail_fast); + } + + { + span av; + CHECK(av.sub(0).length() == 0); + CHECK_THROW(av.sub(1).length(), fail_fast); + } + + { + span av = arr; + CHECK(av.sub(0).length() == 5); + CHECK(av.sub(1).length() == 4); + CHECK(av.sub(4).length() == 1); + CHECK(av.sub(5).length() == 0); + CHECK_THROW(av.sub(6).length(), fail_fast); + auto av2 = av.sub(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i+2); + } + + { + span av = arr; + CHECK(av.sub(0).length() == 5); + CHECK(av.sub(1).length() == 4); + CHECK(av.sub(4).length() == 1); + CHECK(av.sub(5).length() == 0); + CHECK_THROW(av.sub(6).length(), fail_fast); + auto av2 = av.sub(1); + for (int i = 0; i < 4; ++i) + CHECK(av2[i] == i+2); + } + } + + void AssertNullEmptyProperties(span& av) + { + CHECK(av.length() == 0); + CHECK(av.data() == nullptr); + CHECK(!av); + } + + template + void AssertContentsMatch(T a1, U a2) + { + CHECK(a1.length() == a2.length()); + for (auto i = 0; i < a1.length(); ++i) + CHECK(a1[i] == a2[i]); + } + + TEST(TestNullConstruction) + { + span av; + AssertNullEmptyProperties(av); + + span av2(nullptr); + AssertNullEmptyProperties(av2); + } + + TEST(ArrayConstruction) + { + int a[] = { 1, 2, 3, 4 }; + + span av = { &a[1], 3 }; + CHECK(av.length() == 3); + + span av3 = { a, 2 }; + CHECK(av3.length() == 2); + + span av2 = a; + CHECK(av2.length() == 4); + } + + TEST(NonConstConstConversions) + { + int a[] = { 1, 2, 3, 4 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + span cav = a; + span av = cav; +#else + span av = a; + span cav = av; +#endif + AssertContentsMatch(av, cav); + } + + TEST(FixedSizeConversions) + { + int arr[] = { 1, 2, 3, 4 }; + + // converting to an span from an equal size array is ok + span av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + span av = av4; + } + { + span av = arr; + } + + // initialization or assignment to static span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av2 = arr; + } + { + span av2 = av4; + } +#endif + + { + span av = arr; + span av2 = av; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av = arr; + span av2 = av.as_span(dim<2>(), dim<2>()); + } +#endif + + { + span av = arr; + auto f = [&]() {span av2 = av.as_span(dim<>(2), dim<>(2));}; + CHECK_THROW(f(), fail_fast); + } + + // but doing so explicitly is ok + + // you can convert statically + { + span av2 = {arr, 2}; + } + { + span av2 = av4.first<1>(); + } + + // ...or dynamically + { + // NB: implicit conversion to span from span + span av2 = av4.first(1); + } + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = { 1, 2 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av4 = arr2; + } + { + span av2 = arr2; + span av4 = av2; + } +#endif + { + auto f = [&]() {span av4 = {arr2, 2};}; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + span av = arr2; + auto f = [&](){ span av2 = av; }; + CHECK_THROW(f(), fail_fast); + } + + TEST(AsWriteableBytes) + { + int a[] = { 1, 2, 3, 4 }; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + span av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + span av; + auto wav = av.as_writeable_bytes(); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.bytes() == 0); + } + + { + span av = a; + auto wav = av.as_writeable_bytes(); + CHECK(wav.data() == (byte*)&a[0]); + CHECK(wav.length() == sizeof(a)); + } + } + + TEST(NonConstIterator) + { + int a[] = { 1, 2, 3, 4 }; + + { + span av = a; + auto wav = av.as_writeable_bytes(); + for (auto& b : wav) + { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 0); + } + } + + { + span av = a; + for (auto& n : av) + { + n = 1; + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 1); + } + } + } + + TEST(ArrayViewComparison) + { + { + int arr[10][2]; + auto av1 = as_span(arr); + span av2 = av1; + + CHECK(av1 == av2); + + span av3 = av1.as_span(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + } + + { + auto av1 = nullptr; + auto av2 = nullptr; + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 2, 1 }; // bigger + + span av1 = nullptr; + span av2 = arr; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; + int arr2[] = { 1, 2 }; + span av1 = arr1; + span av2 = arr2; + + CHECK(av1 == av2); + CHECK(!(av1 != av2)); + CHECK(!(av1 < av2)); + CHECK(av1 <= av2); + CHECK(!(av1 > av2)); + CHECK(av1 >= av2); + CHECK(av2 == av1); + CHECK(!(av2 != av1)); + CHECK(!(av2 < av1)); + CHECK(av2 <= av1); + CHECK(!(av2 > av1)); + CHECK(av2 >= av1); + } + + { + int arr[] = { 1, 2, 3 }; + + span av1 = { &arr[0], 2 }; // shorter + span av2 = arr; // longer + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + + { + int arr1[] = { 1, 2 }; // smaller + int arr2[] = { 2, 1 }; // bigger + + span av1 = arr1; + span av2 = arr2; + + CHECK(av1 != av2); + CHECK(av2 != av1); + CHECK(!(av1 == av2)); + CHECK(!(av2 == av1)); + CHECK(av1 < av2); + CHECK(!(av2 < av1)); + CHECK(av1 <= av2); + CHECK(!(av2 <= av1)); + CHECK(av2 > av1); + CHECK(!(av1 > av2)); + CHECK(av2 >= av1); + CHECK(!(av1 >= av2)); + } + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp new file mode 100644 index 0000000..dc3ccf5 --- /dev/null +++ b/tests/string_span_tests.cpp @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include +#include + +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() == s.length()); + } + + TEST(TestConstructFromStdVector) + { + std::vector vec(5, 'h'); + string_span<> v = vec; + CHECK(v.length() == vec.size()); + } + + TEST(TestStackArrayConstruction) + { + wchar_t stack_string[] = L"Hello"; + + { + cwstring_span<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + CHECK(v.used_length() == v.length()); + } + + { + cwstring_span<> v = stack_string; + CHECK(v.length() == 6); + CHECK(v.used_length() == v.length()); + } + + { + wstring_span<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + CHECK(v.used_length() == v.length()); + } + + { + wstring_span<> v = stack_string; + CHECK(v.length() == 6); + CHECK(v.used_length() == v.length()); + } + } + + TEST(TestConstructFromConstCharPointer) + { + const char* s = "Hello"; + cstring_span<> v = ensure_z(s); + CHECK(v.length() == 5); + CHECK(v.used_length() == v.length()); + } + + 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); +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> v2 = v; + string_span<> v3 = "Hello"; +#endif + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp deleted file mode 100644 index e553ccd..0000000 --- a/tests/string_view_tests.cpp +++ /dev/null @@ -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 -#include -#include -#include - -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 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(); -} -- cgit v1.2.3 From caabb40440f44b24b573a0822433ea758b469b96 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 4 Nov 2015 13:50:57 -0800 Subject: Rename of array_view/string_view in readme. Knew I'd miss something! --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c687673..6e84a82 100644 --- a/README.md +++ b/README.md @@ -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`, `string_span`, `owner<>` and others. The entire implementation is provided inline in the headers under the [include](./include) directory. -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 -- cgit v1.2.3 From 4e4882bda8dee92d05c11708a016f3409abed963 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 5 Nov 2015 09:29:30 -0800 Subject: Added workaround for MSVC 2013 compiler bug. --- include/string_span.h | 56 +++++++++++++++++++++++++++++++++++++++++---- tests/string_span_tests.cpp | 13 +++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index beccee4..133712e 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -22,6 +22,16 @@ #include "span.h" #include +// VS 2013 workarounds +#ifdef _MSC_VER +#if _MSC_VER <= 1800 + +#pragma push_macro("GSL_MSVC_HAS_TYPE_DEDUCTION_BUG") +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +#endif // _MSC_VER <= 1800 +#endif // _MSC_VER + namespace gsl { // @@ -34,16 +44,16 @@ namespace gsl // type system for these types that will not either incur significant runtime costs or // (sometimes needlessly) break existing programs when introduced. // -template +template using czstring = const char*; -template +template using cwzstring = const wchar_t*; -template +template using zstring = char*; -template +template using wzstring = wchar_t*; // @@ -134,12 +144,37 @@ basic_string_span::type, dy // // to_string() allow (explicit) conversions from string_span to string // -template +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +template std::basic_string::type> to_string(basic_string_span view) +{ + return{ view.data(), static_cast(view.length()) }; +} + +#else + +std::string to_string(cstring_span<> view) +{ + return{ view.data(), view.length() }; +} + +std::string to_string(string_span<> view) +{ + return{ view.data(), view.length() }; +} + +std::wstring to_string(cwstring_span<> view) +{ + return{ view.data(), view.length() }; +} + +std::wstring to_string(wstring_span<> view) { return{ view.data(), view.length() }; } +#endif template class basic_zstring_builder @@ -178,4 +213,15 @@ template using wzstring_builder = basic_zstring_builder; } +// VS 2013 workarounds +#ifdef _MSC_VER +#if _MSC_VER <= 1800 + +#pragma pop_macro("GSL_MSVC_HAS_TYPE_DEDUCTION_BUG") +#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +#endif // _MSC_VER <= 1800 +#endif // _MSC_VER + + #endif // GSL_STRING_SPAN_H diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index dc3ccf5..ab48fcb 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -22,6 +22,7 @@ using namespace std; using namespace gsl; + SUITE(string_span_tests) { @@ -104,6 +105,18 @@ SUITE(string_span_tests) 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(s2.length() == v.length()); + CHECK(s2.length() == 5); + } } int main(int, const char *[]) -- cgit v1.2.3 From 31dd90ef31728549fbbb1b6e1f4d53be67175260 Mon Sep 17 00:00:00 2001 From: Rostislav Khlebnikov Date: Thu, 5 Nov 2015 17:56:50 +0000 Subject: added missing inlines --- include/string_span.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 133712e..68095fa 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -154,22 +154,22 @@ std::basic_string::type> to_string(basic_strin #else -std::string to_string(cstring_span<> view) +inline std::string to_string(cstring_span<> view) { return{ view.data(), view.length() }; } -std::string to_string(string_span<> view) +inline std::string to_string(string_span<> view) { return{ view.data(), view.length() }; } -std::wstring to_string(cwstring_span<> view) +inline std::wstring to_string(cwstring_span<> view) { return{ view.data(), view.length() }; } -std::wstring to_string(wstring_span<> view) +inline std::wstring to_string(wstring_span<> view) { return{ view.data(), view.length() }; } -- cgit v1.2.3 From 670ffbeb1133caff2f95db3834824c379aa9b0a8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 11 Nov 2015 10:37:43 -0800 Subject: Added note regarding C++14 support requirements. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e84a82..5743bb3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repo contains Microsoft's implementation of GSL. The library includes types like `span`, `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/span.h](./include/span.h)), it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library. -- cgit v1.2.3 From fc289930ee37a5bd34bf7198fc30948f455cc2f3 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 11 Nov 2015 20:48:42 -0800 Subject: Cast size_type for string_span ctor from container. --- include/string_span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_span.h b/include/string_span.h index 68095fa..7194c85 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -138,7 +138,7 @@ basic_string_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0] template basic_string_span::type, dynamic_range> ensure_z(Cont& cont) { - return ensure_z(cont.data(), cont.length()); + return ensure_z(cont.data(), static_cast(cont.length())); } // -- cgit v1.2.3 From 557e6693b560ca491f285712081632bf4e93bda9 Mon Sep 17 00:00:00 2001 From: Vladislav Yaroslavlev Date: Thu, 12 Nov 2015 10:44:41 +0300 Subject: Add copy assignment operator to `strided_bounds` `strided_bounds` defines copy constructor, but lacks copy assignment operator --- include/span.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/span.h b/include/span.h index 74da1aa..cb045cb 100644 --- a/include/span.h +++ b/include/span.h @@ -685,6 +685,8 @@ public: constexpr strided_bounds(const strided_bounds &) noexcept = default; + constexpr strided_bounds & operator=(const strided_bounds &) noexcept = default; + constexpr strided_bounds(const value_type(&values)[rank], index_type strides) : m_extents(values), m_strides(std::move(strides)) {} -- cgit v1.2.3 From 995cfdf1cbd54a487008910ced5103d69e9e1acb Mon Sep 17 00:00:00 2001 From: Vladislav Yaroslavlev Date: Thu, 12 Nov 2015 10:46:21 +0300 Subject: Add copy assignment operator to `BoundsRanges` `BoundsRanges` defines copy constructor, but lacks `=` operator --- include/span.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/span.h b/include/span.h index cb045cb..adcded9 100644 --- a/include/span.h +++ b/include/span.h @@ -292,6 +292,7 @@ namespace details {} BoundsRanges (const BoundsRanges&) = default; + BoundsRanges& operator=(const BoundsRanges&) = default; BoundsRanges(const std::ptrdiff_t* const) { } BoundsRanges() = default; -- cgit v1.2.3 From 550361cf46fecf99ecfcb8d6ce563f40ab25fc88 Mon Sep 17 00:00:00 2001 From: Olaf van der Spek Date: Thu, 12 Nov 2015 10:23:56 +0100 Subject: Sort includes --- include/span.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/span.h b/include/span.h index 74da1aa..b832e19 100644 --- a/include/span.h +++ b/include/span.h @@ -19,18 +19,18 @@ #ifndef GSL_SPAN_H #define GSL_SPAN_H -#include -#include +#include +#include #include #include +#include +#include #include +#include #include +#include #include #include -#include -#include -#include -#include #include "fail_fast.h" #ifdef _MSC_VER -- cgit v1.2.3 From a998a9b33b3cbcc12595391de8d62a33eb14d290 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 12 Nov 2015 18:57:23 -0800 Subject: Turned on Level 3 warnings for MSVC. --- include/span.h | 1 + tests/CMakeLists.txt | 1 + tests/span_tests.cpp | 9 +++++++-- tests/string_span_tests.cpp | 6 +++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/span.h b/include/span.h index 32cf828..f4a57d9 100644 --- a/include/span.h +++ b/include/span.h @@ -55,6 +55,7 @@ // turn off some misguided warnings #pragma warning(push) #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable: 4512) // warns that assignment op could not be generated #endif // _MSC_VER <= 1800 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3435a7f..d49cf93 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,6 +14,7 @@ add_definitions(-DGSL_THROWS_FOR_TESTING) 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) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 4c21116..ae14f20 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -198,6 +198,7 @@ SUITE(span_tests) string str = "ttttttttttttttt"; // size = 15 auto t = str.data(); + (void)t; auto av3 = as_span(str); overloaded_func(av3.as_span(dim<>(1), dim<3>(), dim<5>()), 't'); } @@ -223,12 +224,12 @@ SUITE(span_tests) } } - template void fn(const Bounds& b) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } + template void fn(const Bounds&) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } TEST (span_reshape_test) { int a[3][4][5]; auto av = as_span(a); - fn(av.bounds()); + fn(av.bounds()); auto av2 = av.as_span(dim<60>()); auto av3 = av2.as_span(dim<3>(), dim<4>(), dim<5>()); auto av4 = av3.as_span(dim<4>(), dim<>(3), dim<5>()); @@ -755,6 +756,7 @@ SUITE(span_tests) CHECK_THROW(empty_av.cbegin()[0], fail_fast); for (auto& v : empty_av) { + (void)v; CHECK(false); } } @@ -767,6 +769,7 @@ SUITE(span_tests) CHECK_THROW(empty_av.cbegin()[0], fail_fast); for (auto& v : empty_av) { + (void)v; CHECK(false); } } @@ -782,6 +785,7 @@ SUITE(span_tests) for (auto& v : empty_sav) { + (void)v; CHECK(false); } } @@ -796,6 +800,7 @@ SUITE(span_tests) for (auto& v : empty_sav) { + (void)v; CHECK(false); } } diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index ab48fcb..5d4681a 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -41,14 +41,14 @@ SUITE(string_span_tests) { std::string s = "Hello there world"; cstring_span<> v = s; - CHECK(v.length() == s.length()); + CHECK(v.length() == static_cast::size_type>(s.length())); } TEST(TestConstructFromStdVector) { std::vector vec(5, 'h'); string_span<> v = vec; - CHECK(v.length() == vec.size()); + CHECK(v.length() == static_cast::size_type>(vec.size())); } TEST(TestStackArrayConstruction) @@ -114,7 +114,7 @@ SUITE(string_span_tests) char stack_string[] = "Hello"; cstring_span<> v = ensure_z(stack_string); auto s2 = gsl::to_string(v); - CHECK(s2.length() == v.length()); + CHECK(static_cast::size_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); } } -- cgit v1.2.3 From fa056f67e8c0c3ef27df5da241c714cc0f45d71a Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 13 Nov 2015 03:27:53 +0000 Subject: Enabled -Wall for gcc and clang. --- tests/CMakeLists.txt | 1 + tests/bounds_tests.cpp | 10 +++++++--- tests/notnull_tests.cpp | 3 ++- tests/span_tests.cpp | 33 +++++++++++++++++++++------------ tests/string_span_tests.cpp | 1 + 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d49cf93..b16821b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,6 +26,7 @@ else() else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() + add_compile_options(-Wall -Wno-missing-braces) endif() if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index 53c44d1..0665260 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -32,7 +32,9 @@ SUITE(bounds_test) { for (auto point : static_bounds { 2 }) { - for (decltype(point)::size_type j = 0; j < decltype(point)::rank; j++) + for (decltype(point)::size_type j = 0; + j < static_cast(decltype(point)::rank); + j++) { use(j); use(point[j]); @@ -44,6 +46,7 @@ SUITE(bounds_test) { static_bounds<3, 4, 5> b; auto a = b.slice(); + (void)a; static_bounds<4, dynamic_range, 2> x{ 4 }; x.slice().slice(); } @@ -53,7 +56,7 @@ SUITE(bounds_test) static_bounds<4, dynamic_range, 2> bounds{ 3 }; auto itr = bounds.begin(); - + (void)itr; #ifdef CONFIRM_COMPILATION_ERRORS span av(nullptr, bounds); @@ -70,13 +73,14 @@ SUITE(bounds_test) { static_bounds<7, 4, 2> b1; static_bounds<7, dynamic_range, 2> b2 = b1; - + (void)b2; #ifdef CONFIRM_COMPILATION_ERRORS static_bounds<7, dynamic_range, 1> b4 = b2; #endif static_bounds b3 = b1; static_bounds<7, 4, 2> b4 = b3; + (void)b4; static_bounds b11; diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index a9624b8..67b478a 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -69,7 +69,8 @@ SUITE(NotNullTests) MyDerived derived; Unrelated unrelated; not_null u = &unrelated; - not_null p = &derived; + (void)u; + not_null p = &derived; not_null q = &base; q = p; // allowed with heterogeneous copy ctor CHECK(q == p); diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index ae14f20..6a67b02 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -27,7 +27,6 @@ using namespace gsl; namespace { - void use(int&) {} struct BaseClass {}; struct DerivedClass : BaseClass {}; } @@ -82,6 +81,7 @@ SUITE(span_tests) span avb = avd; #endif span avcd = avd; + (void)avcd; } TEST(boundary_checks) @@ -214,9 +214,11 @@ SUITE(span_tests) const std::array arr = {0.0, 0.0, 0.0}; auto cv = as_span(arr); + (void)cv; vector vec(3); auto dv = as_span(vec); + (void)dv; #ifdef CONFIRM_COMPILATION_ERRORS auto dv2 = as_span(std::move(vec)); @@ -262,6 +264,7 @@ SUITE(span_tests) 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) @@ -1417,7 +1420,7 @@ SUITE(span_tests) CHECK_THROW(av1[10][3][4], fail_fast); span av2 = av1.as_span(dim<>(5), dim<6>(), dim<4>()); - + (void)av2; } TEST(span_sub) @@ -1429,7 +1432,7 @@ SUITE(span_tests) CHECK((av.sub<2,2>().bounds() == static_bounds<2>())); CHECK((av.sub<2,2>().length() == 2)); CHECK(av.sub(2,2).length() == 2); - CHECK(av.sub(2,3).length() == 3); + CHECK(av.sub(2,3).length() == 3); } @@ -1445,16 +1448,16 @@ SUITE(span_tests) CHECK((av.sub<0,5>().bounds() == static_bounds<5>())); CHECK((av.sub<0,5>().length() == 5)); CHECK(av.sub(0,5).length() == 5); - CHECK_THROW(av.sub(0,6).length(), fail_fast); - CHECK_THROW(av.sub(1,5).length(), fail_fast); + CHECK_THROW(av.sub(0,6).length(), fail_fast); + CHECK_THROW(av.sub(1,5).length(), fail_fast); } { span av = arr; CHECK((av.sub<5,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<5, 0>().length() == 0)); - CHECK(av.sub(5,0).length() == 0); - CHECK_THROW(av.sub(6,0).length(), fail_fast); + CHECK((av.sub<5, 0>().length() == 0)); + CHECK(av.sub(5,0).length() == 0); + CHECK_THROW(av.sub(6,0).length(), fail_fast); } { @@ -1462,7 +1465,7 @@ SUITE(span_tests) CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); CHECK((av.sub<0,0>().length() == 0)); CHECK(av.sub(0,0).length() == 0); - CHECK_THROW((av.sub<1,0>().length()), fail_fast); + CHECK_THROW((av.sub<1,0>().length()), fail_fast); } { @@ -1559,9 +1562,11 @@ SUITE(span_tests) // converting to dynamic_range a_v is always ok { span av = av4; + (void)av; } { span av = arr; + (void)av; } // initialization or assignment to static span that REDUCES size is NOT ok @@ -1577,6 +1582,7 @@ SUITE(span_tests) { span av = arr; span av2 = av; + (void)av2; } #ifdef CONFIRM_COMPILATION_ERRORS @@ -1588,7 +1594,7 @@ SUITE(span_tests) { span av = arr; - auto f = [&]() {span av2 = av.as_span(dim<>(2), dim<>(2));}; + auto f = [&]() {span av2 = av.as_span(dim<>(2), dim<>(2)); (void)av2; }; CHECK_THROW(f(), fail_fast); } @@ -1597,15 +1603,18 @@ SUITE(span_tests) // you can convert statically { span av2 = {arr, 2}; + (void)av2; } { span av2 = av4.first<1>(); + (void)av2; } // ...or dynamically { // NB: implicit conversion to span from span span av2 = av4.first(1); + (void)av2; } // initialization or assignment to static span that requires size INCREASE is not ok. @@ -1621,13 +1630,13 @@ SUITE(span_tests) } #endif { - auto f = [&]() {span av4 = {arr2, 2};}; + auto f = [&]() {span av4 = {arr2, 2}; (void)av4; }; CHECK_THROW(f(), fail_fast); } // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one span av = arr2; - auto f = [&](){ span av2 = av; }; + auto f = [&](){ span av2 = av; (void)av2; }; CHECK_THROW(f(), fail_fast); } diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 5d4681a..efdf0ff 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -100,6 +100,7 @@ SUITE(string_span_tests) { 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"; -- cgit v1.2.3 From b9565e50ce9d01173243dfcda3e108e5b9a8e75e Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 12 Nov 2015 19:36:34 -0800 Subject: Update CMakeLists.txt --- tests/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b16821b..5529cda 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,13 +20,12 @@ else() 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() - add_compile_options(-Wall -Wno-missing-braces) endif() if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) -- cgit v1.2.3 From 8aa42487225a537ddedac668ec14e179bb481935 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 11 Nov 2015 12:41:11 -0800 Subject: Removing basic_span base class from span --- include/span.h | 326 +++++++++++++++++++++++++++++++++++---------------- tests/span_tests.cpp | 19 +-- 2 files changed, 238 insertions(+), 107 deletions(-) diff --git a/include/span.h b/include/span.h index f4a57d9..cbc766b 100644 --- a/include/span.h +++ b/include/span.h @@ -111,19 +111,19 @@ public: std::copy(values, values + Rank, elems); } -#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) - {} -#else - template::value>> - constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } - {} -#endif +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) + {} +#else + template::value>> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} +#endif constexpr index(const index& other) noexcept = default; @@ -1039,9 +1039,9 @@ namespace details } // namespace details -template +template class contiguous_span_iterator; -template +template class general_span_iterator; enum class byte : std::uint8_t {}; @@ -1235,21 +1235,21 @@ class strided_span; namespace details { template - struct ArrayViewTypeTraits + struct SpanTypeTraits { using value_type = T; using size_type = size_t; }; template - struct ArrayViewTypeTraits::type> + struct SpanTypeTraits::type> { using value_type = typename Traits::span_traits::value_type; using size_type = typename Traits::span_traits::size_type; }; template - struct ArrayViewArrayTraits { + struct SpanArrayTraits { using type = span; using value_type = T; using bounds_type = static_bounds; @@ -1257,7 +1257,7 @@ namespace details using reference = T&; }; template - struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + struct SpanArrayTraits : SpanArrayTraits {}; template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size @@ -1322,108 +1322,122 @@ namespace details template -class span : public basic_span > +class span { - template + template friend class span; - using Base = basic_span>; - public: - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - using Base::rank; + using BoundsType = static_bounds; + static const size_t rank = BoundsType::rank; + using bounds_type = BoundsType; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = ValueType*; + using reference = ValueType&; + using iterator = contiguous_span_iterator; + using const_span = span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; + +private: + pointer m_pdata; + bounds_type m_bounds; + + friend iterator; + friend const_iterator; public: - // basic - constexpr span(pointer ptr, size_type size) : Base(ptr, bounds_type{ size }) - {} - constexpr span(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + constexpr span(pointer data, bounds_type bound) noexcept + : m_pdata(data), m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + + constexpr span(pointer ptr, size_type size) : span(ptr, bounds_type{ size }) {} - constexpr span(std::nullptr_t) : Base(nullptr, bounds_type{}) + constexpr span(std::nullptr_t) : span(nullptr, bounds_type{}) {} - constexpr span(std::nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + constexpr span(std::nullptr_t, size_type size) : span(nullptr, bounds_type{}) { fail_fast_assert(size == 0); } // default template > - constexpr span() : Base(nullptr, bounds_type()) + constexpr span() : span(nullptr, bounds_type()) {} // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template - /*typename Dummy = std::enable_if_t::value>*/> - constexpr span(T* const& data, size_type size) : Base(data, typename Helper::bounds_type{size}) + template + /*typename Dummy = std::enable_if_t::value>*/ + > + constexpr span(T* const& data, size_type size) : span(data, typename Helper::bounds_type{size}) {} // from n-dimensions static array - template , - typename = std::enable_if_t::value>> - constexpr span (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + template , + typename = std::enable_if_t::value> + > + constexpr span (T (&arr)[N]) : span(arr, typename Helper::bounds_type()) {} // from n-dimensions static array with size - template , - typename = std::enable_if_t::value> + template , + typename = std::enable_if_t::value> > - constexpr span(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{size}) + constexpr span(T(&arr)[N], size_type size) : span(arr, typename Helper::bounds_type{size}) { fail_fast_assert(size <= N); } // from std array template , typename Base::bounds_type>::value> + typename Dummy = std::enable_if_t, bounds_type>::value> > - constexpr span (std::array, N> & arr) : Base(arr.data(), static_bounds()) + constexpr span (std::array, N> & arr) : span(arr.data(), static_bounds()) {} template , typename Base::bounds_type>::value + typename Dummy = std::enable_if_t, bounds_type>::value && std::is_const::value> > - constexpr span (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + constexpr span (const std::array, N> & arr) : span(arr.data(), static_bounds()) {} // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity template ::value - && details::LessThan::value> + && details::LessThan::value> > // remove literal 0 case - constexpr span (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + constexpr span (pointer begin, Ptr end) : span(begin, details::newBoundsHelper(static_cast(end) - begin)) {} // from containers. It must has .size() and .data() two function signatures template ::value - && std::is_convertible::value + && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > - constexpr span (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + constexpr span (Cont& cont) : span(static_cast(cont.data()), details::newBoundsHelper(cont.size())) {} constexpr span(const span &) = default; // convertible template >, - typename OtherBaseType = basic_span>, - typename Dummy = std::enable_if_t::value> + typename OtherBounds = static_bounds, + typename Dummy = std::enable_if_t::value && std::is_convertible::value> > - constexpr span(const span &av) - : Base(static_cast::Base&>(av)) + constexpr span(const span& other) + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) {} // reshape @@ -1454,18 +1468,18 @@ public: // from bytes array template::value, typename = std::enable_if_t> - constexpr auto as_span() const noexcept -> span(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + constexpr auto as_span() const noexcept -> span(static_cast(bounds_type::static_size) / sizeof(U)) : dynamic_range)> { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } template::value, typename = std::enable_if_t> - constexpr auto as_span() const noexcept -> span(static_cast(Base::bounds_type::static_size) / sizeof(U)) : dynamic_range)> + constexpr auto as_span() const noexcept -> span(static_cast(bounds_type::static_size) / sizeof(U)) : dynamic_range)> { - static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast(sizeof(U)) == 0), + static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); fail_fast_assert((this->bytes() % sizeof(U)) == 0); return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; @@ -1539,27 +1553,143 @@ public: constexpr strided_span section(index_type origin, index_type extents) const { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(Base::bounds())} }; + return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(bounds())} }; } - constexpr reference operator[](const index_type& idx) const + constexpr reference operator[](const index_type& idx) const { - return Base::operator[](idx); + return m_pdata[m_bounds.linearize(idx)]; } - template 1), typename Dummy = std::enable_if_t> - constexpr span operator[](size_type idx) const + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds() }; + fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + const size_type ridx = idx * m_bounds.stride(); + + fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + return Ret{ m_pdata + ridx, m_bounds.slice() }; } - using Base::operator==; - using Base::operator!=; - using Base::operator<; - using Base::operator<=; - using Base::operator>; - using Base::operator>=; + constexpr bounds_type bounds() const noexcept + { + return m_bounds; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + return m_bounds.template extent(); + } + + constexpr size_type size() const noexcept + { + return m_bounds.size(); + } + + constexpr pointer data() const noexcept + { + return m_pdata; + } + + constexpr operator bool() const noexcept + { + return m_pdata != nullptr; + } + + constexpr iterator begin() const + { + return iterator{ this, true }; + } + + constexpr iterator end() const + { + return iterator{ this, false }; + } + + constexpr const_iterator cbegin() const + { + return const_iterator{ reinterpret_cast(this), true }; + } + + constexpr const_iterator cend() const + { + return const_iterator{ reinterpret_cast(this), false }; + } + + constexpr reverse_iterator rbegin() const + { + return reverse_iterator{ end() }; + } + + constexpr reverse_iterator rend() const + { + return reverse_iterator{ begin() }; + } + + constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator{ cend() }; + } + + constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator{ cbegin() }; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator== (const span & other) const noexcept + { + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!= (const span & other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator< (const span & other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<= (const span & other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator> (const span & other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>= (const span & other) const noexcept + { + return !(*this < other); + } + +private: + + template + constexpr span(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept + : m_pdata(reinterpret_cast(data)), m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + + template + constexpr span as_span(const static_bounds &bounds) + { + details::verifyBoundsReshape(m_bounds, bounds); + return{ m_pdata, bounds }; + } }; template @@ -1569,13 +1699,13 @@ constexpr auto as_span(T* const& ptr, dim... args) -> span -constexpr auto as_span (T* arr, std::ptrdiff_t len) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_span (T* arr, std::ptrdiff_t len) -> typename details::SpanArrayTraits::type { return {reinterpret_cast*>(arr), len}; } template -constexpr auto as_span (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +constexpr auto as_span (T (&arr)[N]) -> typename details::SpanArrayTraits::type { return {arr}; } @@ -1725,26 +1855,26 @@ private: } }; -template -class contiguous_span_iterator : public std::iterator +template +class contiguous_span_iterator : public std::iterator { - using Base = std::iterator; + using Base = std::iterator; public: using typename Base::reference; using typename Base::pointer; using typename Base::difference_type; private: - template - friend class basic_span; + template + friend class span; pointer m_pdata; - const ArrayView * m_validator; + const Span* m_validator; void validateThis() const { fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } - contiguous_span_iterator (const ArrayView *container, bool isbegin) : + contiguous_span_iterator (const Span* container, bool isbegin) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} public: reference operator*() const noexcept @@ -1840,16 +1970,16 @@ public: } }; -template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, const contiguous_span_iterator& rhs) noexcept +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, const contiguous_span_iterator& rhs) noexcept { return rhs + n; } -template -class general_span_iterator : public std::iterator +template +class general_span_iterator : public std::iterator { - using Base = std::iterator; + using Base = std::iterator; public: using typename Base::reference; using typename Base::pointer; @@ -1859,9 +1989,9 @@ private: template friend class basic_span; - const ArrayView * m_container; - typename ArrayView::bounds_type::iterator m_itr; - general_span_iterator(const ArrayView *container, bool isbegin) : + const Span * m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span *container, bool isbegin) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) {} public: @@ -1956,8 +2086,8 @@ public: } }; -template -general_span_iterator operator+(typename general_span_iterator::difference_type n, const general_span_iterator& rhs) noexcept +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, const general_span_iterator& rhs) noexcept { return rhs + n; } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 6a67b02..ecb2c4e 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -323,28 +323,29 @@ SUITE(span_tests) CHECK(sav[1] == 2); #if _MSC_VER > 1800 - strided_span sav_c{ {src}, {2, 1} }; -#else + //strided_span sav_c{ {src}, {2, 1} }; strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; -#endif +#else + strided_span sav_c{ span{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 sav_v{ {src}, {2, 1} }; -#else +#else strided_span sav_v{ span{src}, strided_bounds<1>{2, 1} }; -#endif +#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 sav_cv{ {src}, {2, 1} }; -#else +#else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; -#endif +#endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); CHECK(sav_cv[1] == 2); @@ -363,7 +364,7 @@ SUITE(span_tests) strided_span sav_cv{ {src}, {2, 1} }; #else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; -#endif +#endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); @@ -383,7 +384,7 @@ SUITE(span_tests) strided_span sav_cv{ {src}, {2, 1} }; #else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; -#endif +#endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); CHECK(sav_cv[1] == 2); -- cgit v1.2.3 From f510025109a75035ba9c99616ab5232f86c0b51d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 12 Nov 2015 12:48:49 -0800 Subject: Removed basic_span class --- include/span.h | 463 ++++++++++++++++++++++----------------------------- tests/span_tests.cpp | 4 +- 2 files changed, 201 insertions(+), 266 deletions(-) diff --git a/include/span.h b/include/span.h index cbc766b..be7e74f 100644 --- a/include/span.h +++ b/include/span.h @@ -1045,174 +1045,6 @@ template class general_span_iterator; enum class byte : std::uint8_t {}; -template -class basic_span -{ -public: - static const size_t rank = BoundsType::rank; - using bounds_type = BoundsType; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = ValueType*; - using reference = ValueType&; - using iterator = std::conditional_t::value, contiguous_span_iterator, general_span_iterator>; - using const_iterator = std::conditional_t::value, contiguous_span_iterator>, general_span_iterator>>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = std::conditional_t>; - -private: - pointer m_pdata; - bounds_type m_bounds; - -public: - constexpr bounds_type bounds() const noexcept - { - return m_bounds; - } - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_bounds.template extent(); - } - constexpr size_type size() const noexcept - { - return m_bounds.size(); - } - constexpr reference operator[](const index_type& idx) const - { - return m_pdata[m_bounds.linearize(idx)]; - } - constexpr pointer data() const noexcept - { - return m_pdata; - } - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); - const size_type ridx = idx * m_bounds.stride(); - - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); - return Ret {m_pdata + ridx, m_bounds.slice()}; - } - - constexpr operator bool () const noexcept - { - return m_pdata != nullptr; - } - - constexpr iterator begin() const - { - return iterator {this, true}; - } - constexpr iterator end() const - { - return iterator {this, false}; - } - constexpr const_iterator cbegin() const - { - return const_iterator {reinterpret_cast *>(this), true}; - } - constexpr const_iterator cend() const - { - return const_iterator {reinterpret_cast *>(this), false}; - } - - constexpr reverse_iterator rbegin() const - { - return reverse_iterator {end()}; - } - constexpr reverse_iterator rend() const - { - return reverse_iterator {begin()}; - } - constexpr const_reverse_iterator crbegin() const - { - return const_reverse_iterator {cend()}; - } - constexpr const_reverse_iterator crend() const - { - return const_reverse_iterator {cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator== (const basic_span & other) const noexcept - { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!= (const basic_span & other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator< (const basic_span & other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<= (const basic_span & other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator> (const basic_span & other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>= (const basic_span & other) const noexcept - { - return !(*this < other); - } - -public: - template ::value - && std::is_convertible::value>> - constexpr basic_span(const basic_span & other ) noexcept - : m_pdata(other.m_pdata), m_bounds(other.m_bounds) - { - } -protected: - - constexpr basic_span(pointer data, bounds_type bound) noexcept - : m_pdata(data) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_span(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept - : m_pdata(reinterpret_cast(data)) - , m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - template - constexpr basic_span as_span(const DestBounds &bounds) - { - details::verifyBoundsReshape(m_bounds, bounds); - return {m_pdata, bounds}; - } -private: - - friend iterator; - friend const_iterator; - template - friend class basic_span; -}; - template struct dim { @@ -1328,15 +1160,14 @@ class span friend class span; public: - using BoundsType = static_bounds; - static const size_t rank = BoundsType::rank; - using bounds_type = BoundsType; + using bounds_type = static_bounds; + static const size_t rank = bounds_type::rank; using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; using value_type = ValueType; using const_value_type = std::add_const_t; - using pointer = ValueType*; - using reference = ValueType&; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; using iterator = contiguous_span_iterator; using const_span = span; using const_iterator = contiguous_span_iterator; @@ -1353,40 +1184,45 @@ private: public: - constexpr span(pointer data, bounds_type bound) noexcept - : m_pdata(data), m_bounds(std::move(bound)) + constexpr span(pointer data, bounds_type bounds) noexcept + : m_pdata(data), m_bounds(std::move(bounds)) { fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } - constexpr span(pointer ptr, size_type size) : span(ptr, bounds_type{ size }) + constexpr span(pointer ptr, size_type size) noexcept + : span(ptr, bounds_type{ size }) {} - constexpr span(std::nullptr_t) : span(nullptr, bounds_type{}) + constexpr span(std::nullptr_t) noexcept + : span(nullptr, bounds_type{}) {} - constexpr span(std::nullptr_t, size_type size) : span(nullptr, bounds_type{}) + constexpr span(std::nullptr_t, size_type size) noexcept + : span(nullptr, bounds_type{}) { fail_fast_assert(size == 0); } // default template > - constexpr span() : span(nullptr, bounds_type()) + constexpr span() noexcept + : span(nullptr, bounds_type()) {} // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template + template , + typename Dummy = std::enable_if_t>::value> /*typename Dummy = std::enable_if_t::value>*/ > - constexpr span(T* const& data, size_type size) : span(data, typename Helper::bounds_type{size}) + constexpr span(T* const& data, size_type size) : span(reinterpret_cast(data), typename Helper::bounds_type{size}) {} // from n-dimensions static array template , typename = std::enable_if_t::value> > - constexpr span (T (&arr)[N]) : span(arr, typename Helper::bounds_type()) + constexpr span (T (&arr)[N]) : span(reinterpret_cast(arr), typename Helper::bounds_type()) {} // from n-dimensions static array with size @@ -1436,13 +1272,13 @@ public: typename OtherBounds = static_bounds, typename Dummy = std::enable_if_t::value && std::is_convertible::value> > - constexpr span(const span& other) + constexpr span(const span& other) noexcept : m_pdata(other.m_pdata), m_bounds(other.m_bounds) {} // reshape // DimCount here is a workaround for a bug in MSVC 2015 - template 0)>> + template 0), typename Dummy = std::enable_if_t> constexpr span as_span(Dimensions2... dims) { using BoundsType = typename span::bounds_type; @@ -1550,19 +1386,19 @@ public: } // section - constexpr strided_span section(index_type origin, index_type extents) const + constexpr strided_span section(index_type origin, index_type extents) const noexcept { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(bounds())} }; } - constexpr reference operator[](const index_type& idx) const + constexpr reference operator[](const index_type& idx) const noexcept { return m_pdata[m_bounds.linearize(idx)]; } template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const + constexpr Ret operator[](size_type idx) const noexcept { fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); const size_type ridx = idx * m_bounds.stride(); @@ -1598,42 +1434,42 @@ public: return m_pdata != nullptr; } - constexpr iterator begin() const + constexpr iterator begin() const noexcept { return iterator{ this, true }; } - constexpr iterator end() const + constexpr iterator end() const noexcept { return iterator{ this, false }; } - constexpr const_iterator cbegin() const + constexpr const_iterator cbegin() const noexcept { return const_iterator{ reinterpret_cast(this), true }; } - constexpr const_iterator cend() const + constexpr const_iterator cend() const noexcept { return const_iterator{ reinterpret_cast(this), false }; } - constexpr reverse_iterator rbegin() const + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{ end() }; } - constexpr reverse_iterator rend() const + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{ begin() }; } - constexpr const_reverse_iterator crbegin() const + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{ cend() }; } - constexpr const_reverse_iterator crend() const + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{ cbegin() }; } @@ -1674,22 +1510,6 @@ public: { return !(*this < other); } - -private: - - template - constexpr span(T *data, std::enable_if_t>::value, bounds_type> bound) noexcept - : m_pdata(reinterpret_cast(data)), m_bounds(std::move(bound)) - { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); - } - - template - constexpr span as_span(const static_bounds &bounds) - { - details::verifyBoundsReshape(m_bounds, bounds); - return{ m_pdata, bounds }; - } }; template @@ -1744,56 +1564,62 @@ constexpr auto as_span(Cont &&arr) -> std::enable_if_t, dynamic_range>> = delete; template -class strided_span : public basic_span> +class strided_span { - using Base = basic_span>; +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; + +private: + pointer m_pdata; + bounds_type m_bounds; - template + friend iterator; + friend const_iterator; + template friend class strided_span; public: - using Base::rank; - using typename Base::bounds_type; - using typename Base::size_type; - using typename Base::pointer; - using typename Base::value_type; - using typename Base::index_type; - using typename Base::iterator; - using typename Base::const_iterator; - using typename Base::reference; - - // from static array of size N - template - strided_span(value_type(&values)[N], bounds_type bounds) : Base(values, std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= N, "Bounds cross data boundaries"); - } - // from raw data - strided_span(pointer ptr, size_type size, bounds_type bounds): Base(ptr, std::move(bounds)) + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : m_pdata(ptr), m_bounds(std::move(bounds)) { + fail_fast_assert((m_bounds.size() > 0 && ptr != nullptr) || m_bounds.size() == 0); fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); } + // from static array of size N + template + constexpr strided_span(value_type(&values)[N], bounds_type bounds) : strided_span(values, N, std::move(bounds)) + {} + // from array view template > - strided_span(span av, bounds_type bounds) : Base(av.data(), std::move(bounds)) - { - fail_fast_assert(this->bounds().total_size() <= av.bounds().total_size(), "Bounds cross data boundaries"); - } + constexpr strided_span(span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + {} // convertible template >, - typename OtherBaseType = basic_span>, - typename Dummy = std::enable_if_t::value> + typename Dummy = std::enable_if_t::value> > - constexpr strided_span(const strided_span &av) : Base(static_cast::Base &>(av)) // static_cast is required + constexpr strided_span(const strided_span& other) + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) {} // convert from bytes - template - strided_span::value, OtherValueType>::type, rank> as_strided_span() const + template + constexpr strided_span::value, OtherValueType>::type, Rank> as_strided_span() const { static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); @@ -1802,54 +1628,163 @@ public: return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; } - strided_span section(index_type origin, index_type extents) const + constexpr strided_span section(index_type origin, index_type extents) const { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(Base::bounds())}}; + return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(bounds())}}; } constexpr reference operator[](const index_type& idx) const { - return Base::operator[](idx); + return m_pdata[m_bounds.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + const size_type ridx = idx * m_bounds.stride(); + + fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + return{ m_pdata + ridx, m_bounds.slice().total_size(), m_bounds.slice() }; + } + + constexpr bounds_type bounds() const noexcept + { + return m_bounds; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, "dimension should be less than Rank (dimension count starts from 0)"); + return m_bounds.template extent(); } - template 1), typename Dummy = std::enable_if_t> - constexpr strided_span operator[](size_type idx) const + constexpr size_type size() const noexcept { - auto ret = Base::operator[](idx); - return{ ret.data(), ret.bounds().total_size(), ret.bounds() }; + return m_bounds.size(); + } + + constexpr pointer data() const noexcept + { + return m_pdata; + } + + constexpr operator bool() const noexcept + { + return m_pdata != nullptr; + } + + constexpr iterator begin() const + { + return iterator{ this, true }; + } + + constexpr iterator end() const + { + return iterator{ this, false }; + } + + constexpr const_iterator cbegin() const + { + return const_iterator{ reinterpret_cast(this), true }; + } + + constexpr const_iterator cend() const + { + return const_iterator{ reinterpret_cast(this), false }; + } + + constexpr reverse_iterator rbegin() const + { + return reverse_iterator{ end() }; + } + + constexpr reverse_iterator rend() const + { + return reverse_iterator{ begin() }; + } + + constexpr const_reverse_iterator crbegin() const + { + return const_reverse_iterator{ cend() }; + } + + constexpr const_reverse_iterator crend() const + { + return const_reverse_iterator{ cbegin() }; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator== (const strided_span& other) const noexcept + { + return m_bounds.size() == other.m_bounds.size() && + (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!= (const strided_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator< (const strided_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<= (const strided_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator> (const strided_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>= (const strided_span& other) const noexcept + { + return !(*this < other); } private: static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) { - fail_fast_assert(extent[rank - 1] >= d && (extent[rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + fail_fast_assert(extent[Rank - 1] >= d && (extent[Rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); index_type ret = extent; - ret[rank - 1] /= d; + ret[Rank - 1] /= d; return ret; } - template > + template > static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + fail_fast_assert(strides[Rank - 1] == 1, "Only strided arrays with regular strides can be resized"); return strides; } - template 1), typename Dummy = std::enable_if_t> + template 1), typename Dummy = std::enable_if_t> static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) { - fail_fast_assert(strides[rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - fail_fast_assert(strides[rank - 2] >= d && (strides[rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + fail_fast_assert(strides[Rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + fail_fast_assert(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); - for (size_t i = rank - 1; i > 0; --i) - fail_fast_assert((strides[i-1] >= strides[i]) && (strides[i-1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); + for (size_t i = Rank - 1; i > 0; --i) + { + fail_fast_assert((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); + } index_type ret = strides / d; - ret[rank - 1] = 1; + ret[Rank - 1] = 1; return ret; } @@ -1986,12 +1921,12 @@ public: using typename Base::difference_type; using typename Base::value_type; private: - template - friend class basic_span; + template + friend class strided_span; - const Span * m_container; + const Span* m_container; typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span *container, bool isbegin) : + general_span_iterator(const Span* container, bool isbegin) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) {} public: @@ -2115,7 +2050,7 @@ general_span_iterator operator+(typename general_span_iterator::diff #endif // _MSC_VER #if defined(GSL_THROWS_FOR_TESTING) -#undef noexcept +#undef noexcept #endif // GSL_THROWS_FOR_TESTING diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index ecb2c4e..8a7c552 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -284,7 +284,7 @@ SUITE(span_tests) CHECK((av_section_2[{0, 1}] == 25)); CHECK((av_section_2[{1, 0}] == 34)); } - + TEST(strided_span_constructors) { // Check stride constructor @@ -1406,7 +1406,7 @@ SUITE(span_tests) } } - TEST(custmized_span_size) + TEST(customized_span_size) { double (*arr)[3][4] = new double[100][3][4]; span av1(arr, 10); -- cgit v1.2.3 From e51eb228ae54e1674da14a73b84541f791ea57f7 Mon Sep 17 00:00:00 2001 From: Lukas Haselsteiner Date: Sun, 15 Nov 2015 23:08:35 +0100 Subject: fixes pragma undef warnings in MSVC2013 --- include/gsl.h | 2 +- include/span.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index e20ac69..bcaf57a 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -219,7 +219,7 @@ namespace std #pragma warning(pop) #ifndef GSL_THROWS_FOR_TESTING -#pragma undef noexcept +#undef noexcept #endif // GSL_THROWS_FOR_TESTING #endif // _MSC_VER <= 1800 diff --git a/include/span.h b/include/span.h index be7e74f..a8cd899 100644 --- a/include/span.h +++ b/include/span.h @@ -2038,7 +2038,7 @@ general_span_iterator operator+(typename general_span_iterator::diff #pragma warning(pop) #ifndef GSL_THROWS_FOR_TESTING -#pragma undef noexcept +#undef noexcept #endif // GSL_THROWS_FOR_TESTING #undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG -- cgit v1.2.3 From 292f81e5efc7a847a2bd69a7dccb289588193a9e Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 17 Nov 2015 15:07:51 -0800 Subject: Tidied up compiler-config macros. --- include/span.h | 29 +++++++++++++++++++++++++---- include/string_span.h | 2 -- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/include/span.h b/include/span.h index be7e74f..6882f40 100644 --- a/include/span.h +++ b/include/span.h @@ -39,16 +39,21 @@ #pragma push_macro("constexpr") #define constexpr /* nothing */ +// MSVC has the potential of bringing in headers where max is a macro +#ifdef max +#pragma push_macro("max") +#undef max +#endif // VS 2013 workarounds #if _MSC_VER <= 1800 -#pragma push_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") +// needed in span.h #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG - // noexcept is not understood #ifndef GSL_THROWS_FOR_TESTING +#pragma push_macro("noexcept") #define noexcept /* nothing */ #endif @@ -63,7 +68,13 @@ // In order to test the library, we need it to throw exceptions that we can catch #ifdef GSL_THROWS_FOR_TESTING + +#ifdef _MSC_VER +#pragma push_macro("noexcept") +#endif + #define noexcept /* nothing */ + #endif // GSL_THROWS_FOR_TESTING @@ -2029,28 +2040,38 @@ general_span_iterator operator+(typename general_span_iterator::diff } // namespace gsl + #ifdef _MSC_VER #undef constexpr #pragma pop_macro("constexpr") +#ifdef max +#pragma pop_macro("max") +#endif + #if _MSC_VER <= 1800 #pragma warning(pop) #ifndef GSL_THROWS_FOR_TESTING #pragma undef noexcept +#pragma pop_macro("noexcept") #endif // GSL_THROWS_FOR_TESTING #undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#pragma pop_macro("GSL_MSVC_HAS_VARIADIC_CTOR_BUG") - #endif // _MSC_VER <= 1800 #endif // _MSC_VER #if defined(GSL_THROWS_FOR_TESTING) + #undef noexcept + +#ifdef _MSC_VER +#pragma pop_macro("noexcept") +#endif + #endif // GSL_THROWS_FOR_TESTING diff --git a/include/string_span.h b/include/string_span.h index 7194c85..b30ebf0 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -26,7 +26,6 @@ #ifdef _MSC_VER #if _MSC_VER <= 1800 -#pragma push_macro("GSL_MSVC_HAS_TYPE_DEDUCTION_BUG") #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #endif // _MSC_VER <= 1800 @@ -217,7 +216,6 @@ using wzstring_builder = basic_zstring_builder; #ifdef _MSC_VER #if _MSC_VER <= 1800 -#pragma pop_macro("GSL_MSVC_HAS_TYPE_DEDUCTION_BUG") #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #endif // _MSC_VER <= 1800 -- cgit v1.2.3 From 106262f1ef68813e64854524634b19c37c4f1cdb Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 17 Nov 2015 19:01:46 -0800 Subject: Remove unnecessary workaround for max macro --- include/span.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/span.h b/include/span.h index a8d0542..7fd711c 100644 --- a/include/span.h +++ b/include/span.h @@ -39,12 +39,6 @@ #pragma push_macro("constexpr") #define constexpr /* nothing */ -// MSVC has the potential of bringing in headers where max is a macro -#ifdef max -#pragma push_macro("max") -#undef max -#endif - // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -2046,10 +2040,6 @@ general_span_iterator operator+(typename general_span_iterator::diff #undef constexpr #pragma pop_macro("constexpr") -#ifdef max -#pragma pop_macro("max") -#endif - #if _MSC_VER <= 1800 #pragma warning(pop) -- cgit v1.2.3 From da75d0e7577aa01d3e307a6454ab500002d73b23 Mon Sep 17 00:00:00 2001 From: Matus Chochlik Date: Wed, 18 Nov 2015 17:43:59 +0100 Subject: Added explicit cast to size_type in span constructor This silences implicit sign conversion warnings when constructing span from containers which return size_t from size(). --- include/span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/span.h b/include/span.h index 7fd711c..8a406ee 100644 --- a/include/span.h +++ b/include/span.h @@ -1267,7 +1267,7 @@ public: && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > - constexpr span (Cont& cont) : span(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + constexpr span (Cont& cont) : span(static_cast(cont.data()), details::newBoundsHelper(static_cast(cont.size()))) {} constexpr span(const span &) = default; -- cgit v1.2.3 From 73ec6886743ef1ab98e857e0db0197a2f3b2a4c8 Mon Sep 17 00:00:00 2001 From: Matus Chochlik Date: Thu, 19 Nov 2015 10:27:08 +0100 Subject: Made conversion of span<> to bool explicit --- include/span.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index 8a406ee..dcce344 100644 --- a/include/span.h +++ b/include/span.h @@ -1434,7 +1434,7 @@ public: return m_pdata; } - constexpr operator bool() const noexcept + constexpr explicit operator bool() const noexcept { return m_pdata != nullptr; } @@ -1676,7 +1676,7 @@ public: return m_pdata; } - constexpr operator bool() const noexcept + constexpr explicit operator bool() const noexcept { return m_pdata != nullptr; } -- cgit v1.2.3 From c95eb57d3f43bf1ad19af79c463a5991a0bd7f6d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Thu, 19 Nov 2015 12:02:06 -0800 Subject: Fixed conversion problem when creating strided_span from span and bounds --- include/span.h | 8 ++++++-- tests/span_tests.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/span.h b/include/span.h index dcce344..74a5e11 100644 --- a/include/span.h +++ b/include/span.h @@ -1610,8 +1610,12 @@ public: {} // from array view - template > - constexpr strided_span(span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + template ::value, + typename Dummy = std::enable_if_t + > + constexpr strided_span(span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) {} // convertible diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8a7c552..a78fe6e 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -333,7 +333,7 @@ SUITE(span_tests) CHECK(sav_c[1] == 2); #if _MSC_VER > 1800 - strided_span sav_v{ {src}, {2, 1} }; + strided_span sav_v{ src, {2, 1} }; #else strided_span sav_v{ span{src}, strided_bounds<1>{2, 1} }; #endif @@ -342,7 +342,7 @@ SUITE(span_tests) CHECK(sav_v[1] == 2); #if _MSC_VER > 1800 - strided_span sav_cv{ {src}, {2, 1} }; + strided_span sav_cv{ src, {2, 1} }; #else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; #endif @@ -361,7 +361,7 @@ SUITE(span_tests) CHECK(sav_c[1] == 2); #if _MSC_VER > 1800 - strided_span sav_cv{ {src}, {2, 1} }; + strided_span sav_cv{ src, {2, 1} }; #else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; #endif @@ -381,7 +381,7 @@ SUITE(span_tests) CHECK(sav_v[1] == 2); #if _MSC_VER > 1800 - strided_span sav_cv{ {src}, {2, 1} }; + strided_span sav_cv{ src, {2, 1} }; #else strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; #endif -- cgit v1.2.3 From e4d8d35af51aab9b072242aef0f57d8ce70625ab Mon Sep 17 00:00:00 2001 From: "Elron A. Yellin" Date: Fri, 20 Nov 2015 17:50:02 -0500 Subject: add as_span overload for basic_string which doesn't have nonconst .data() like other contiguous containers --- include/span.h | 8 ++++++++ tests/span_tests.cpp | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/include/span.h b/include/span.h index 74a5e11..2bf9d20 100644 --- a/include/span.h +++ b/include/span.h @@ -1568,6 +1568,14 @@ template constexpr auto as_span(Cont &&arr) -> std::enable_if_t>::value, span, dynamic_range>> = delete; +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_span(std::basic_string &str) -> span +{ + fail_fast_assert(str.size() < PTRDIFF_MAX); + return {&str[0], static_cast(str.size())}; +} + template class strided_span { diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index a78fe6e..e5078af 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -203,6 +203,15 @@ SUITE(span_tests) overloaded_func(av3.as_span(dim<>(1), dim<3>(), dim<5>()), 't'); } + { + string str; + span strspan = as_span(str); + (void)strspan; + const string cstr; + span cstrspan = as_span(cstr); + (void)cstrspan; + } + { int a[3][4][5]; auto av = as_span(a); -- cgit v1.2.3 From d13f6daa756abd4c8e972395f667398c157728cd Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 20 Nov 2015 16:03:00 -0800 Subject: Refactored to use Expects/Ensures everywhere. --- include/fail_fast.h | 55 -------------------- include/gsl.h | 40 +++++--------- include/gsl_assert.h | 78 ++++++++++++++++++++++++++++ include/span.h | 141 +++++++++++++++++++++++++++----------------------- include/string_span.h | 11 ++-- tests/CMakeLists.txt | 2 +- 6 files changed, 173 insertions(+), 154 deletions(-) delete mode 100644 include/fail_fast.h create mode 100644 include/gsl_assert.h diff --git a/include/fail_fast.h b/include/fail_fast.h deleted file mode 100644 index b9982eb..0000000 --- a/include/fail_fast.h +++ /dev/null @@ -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 - -#if defined(GSL_THROWS_FOR_TESTING) -#include -#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 diff --git a/include/gsl.h b/include/gsl.h index bcaf57a..0575d57 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -19,6 +19,7 @@ #ifndef GSL_GSL_H #define GSL_GSL_H +#include "gsl_assert.h" // Ensures/Expects #include "span.h" // span, strided_span... #include "string_span.h" // zstring, string_span, zstring_builder... #include @@ -27,15 +28,13 @@ // 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 +44,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,12 +57,6 @@ using std::shared_ptr; template 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 // @@ -110,14 +98,14 @@ T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narro // // at() - Bounds-checked way of accessing static arrays, std::array, std::vector // -template -T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; } +template +T& at(T(&arr)[N], size_t index) { Expects(index < N); return arr[index]; } template -T& at(std::array& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; } +T& at(std::array& arr, size_t index) { Expects(index < N); return arr[index]; } template -typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; } +typename Cont::value_type& at(Cont& cont, size_t index) { Expects(index < cont.size()); return cont[index]; } // @@ -181,7 +169,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 +204,14 @@ namespace std #pragma pop_macro("constexpr") #if _MSC_VER <= 1800 -#pragma warning(pop) -#ifndef GSL_THROWS_FOR_TESTING #undef noexcept -#endif // GSL_THROWS_FOR_TESTING +#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 diff --git a/include/gsl_assert.h b/include/gsl_assert.h new file mode 100644 index 0000000..4a40552 --- /dev/null +++ b/include/gsl_assert.h @@ -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 + +// +// 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 + +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 diff --git a/include/span.h b/include/span.h index a8d0542..96a7901 100644 --- a/include/span.h +++ b/include/span.h @@ -31,19 +31,18 @@ #include #include #include -#include "fail_fast.h" +#include +#include "gsl_assert.h" #ifdef _MSC_VER +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable: 4127) // conditional expression is constant + // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr /* nothing */ - -// MSVC has the potential of bringing in headers where max is a macro -#ifdef max -#pragma push_macro("max") -#undef max -#endif +#define constexpr // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -52,7 +51,7 @@ #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG // noexcept is not understood -#ifndef GSL_THROWS_FOR_TESTING +#ifndef GSL_THROWS_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ #endif @@ -66,8 +65,7 @@ #endif // _MSC_VER -// In order to test the library, we need it to throw exceptions that we can catch -#ifdef GSL_THROWS_FOR_TESTING +#ifdef GSL_THROW_ON_CONTRACT_VIOLATION #ifdef _MSC_VER #pragma push_macro("noexcept") @@ -75,7 +73,7 @@ #define noexcept /* nothing */ -#endif // GSL_THROWS_FOR_TESTING +#endif // GSL_THROW_ON_CONTRACT_VIOLATION namespace gsl { @@ -143,14 +141,14 @@ public: // Preconditions: component_idx < rank constexpr reference operator[](size_t component_idx) { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + Expects(component_idx < Rank); // Component index must be less than rank return elems[component_idx]; } // Preconditions: component_idx < rank constexpr const_reference operator[](size_t component_idx) const noexcept { - fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + Expects(component_idx < Rank); // Component index must be less than rank return elems[component_idx]; } @@ -350,7 +348,7 @@ namespace details BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { - fail_fast_assert(0 <= *arr); + Expects(0 <= *arr); } BoundsRanges() : m_bound(0) {} @@ -371,7 +369,7 @@ namespace details size_type linearize(const T& arr) const { const size_type index = this->Base::totalSize() * arr[Dim]; - fail_fast_assert(index < m_bound); + Expects(index < m_bound); return index + this->Base::template linearize(arr); } @@ -425,8 +423,9 @@ namespace details template BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) - { - fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + { + Expects((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + (void)firstLevel; } template @@ -439,7 +438,7 @@ namespace details template size_type linearize(const T& arr) const { - fail_fast_assert(arr[Dim] < CurrentRange, "Index is out of range"); + Expects(arr[Dim] < CurrentRange); // Index is out of range return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); } @@ -590,8 +589,10 @@ public: constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) { - fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); - fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); } constexpr static_bounds() = default; @@ -748,7 +749,7 @@ public: size_type ret = 0; for (size_t i = 0; i < rank; i++) { - fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array ret += idx[i] * m_strides[i]; } return ret; @@ -874,7 +875,7 @@ public: } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" - fail_fast_assert(false); + Expects(false); return *this; } @@ -905,7 +906,8 @@ public: curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } - fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); + //index is out of bounds of the array + Expects(!less(curr, index_type{}) && !less(boundary, curr)); return *this; } @@ -1044,7 +1046,7 @@ namespace details static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); static_assert(std::is_same::value, "The source type must be a contiguous bounds"); static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); - fail_fast_assert(src.size() == dest.size()); + Expects(src.size() == dest.size()); } @@ -1105,13 +1107,13 @@ namespace details template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size { - fail_fast_assert(totalSize <= PTRDIFF_MAX); + Expects(totalSize <= PTRDIFF_MAX); return BoundsType{totalSize}; } template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size { - fail_fast_assert(BoundsType::static_size == totalSize); + Expects(BoundsType::static_size == totalSize); return {}; } template @@ -1198,7 +1200,7 @@ public: constexpr span(pointer data, bounds_type bounds) noexcept : m_pdata(data), m_bounds(std::move(bounds)) { - fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + Expects((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); } constexpr span(pointer ptr, size_type size) noexcept @@ -1212,7 +1214,7 @@ public: constexpr span(std::nullptr_t, size_type size) noexcept : span(nullptr, bounds_type{}) { - fail_fast_assert(size == 0); + Expects(size == 0); } // default @@ -1242,7 +1244,7 @@ public: > constexpr span(T(&arr)[N], size_type size) : span(arr, typename Helper::bounds_type{size}) { - fail_fast_assert(size <= N); + Expects(size <= N); } // from std array @@ -1319,7 +1321,7 @@ public: { static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); + Expects((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } @@ -1328,7 +1330,7 @@ public: { static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be standard layout and its size must match the byte array size"); - fail_fast_assert((this->bytes() % sizeof(U)) == 0); + Expects((this->bytes() % sizeof(U)) == 0); return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; } @@ -1337,13 +1339,13 @@ public: constexpr span first() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data(), Count }; } constexpr span first(size_type count) const noexcept { - fail_fast_assert(count <= this->size()); + Expects(count <= this->size()); return { this->data(), count }; } @@ -1351,13 +1353,13 @@ public: constexpr span last() const noexcept { static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); return { this->data() + this->size() - Count, Count }; } constexpr span last(size_type count) const noexcept { - fail_fast_assert(count <= this->size()); + Expects(count <= this->size()); return { this->data() + this->size() - count, count }; } @@ -1365,13 +1367,13 @@ public: constexpr span sub() const noexcept { static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); + Expects(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); return { this->data() + Offset, Count }; } constexpr span sub(size_type offset, size_type count = dynamic_range) const noexcept { - fail_fast_assert((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); + Expects((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; } @@ -1411,10 +1413,11 @@ public: template 1), typename Ret = std::enable_if_t> constexpr Ret operator[](size_type idx) const noexcept { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + Expects(idx < m_bounds.size()); // index is out of bounds of the array const size_type ridx = idx * m_bounds.stride(); - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + // index is out of bounds of the underlying data + Expects(ridx < m_bounds.total_size()); return Ret{ m_pdata + ridx, m_bounds.slice() }; } @@ -1566,7 +1569,7 @@ template constexpr auto as_span(Cont &arr) -> std::enable_if_t>::value, span, dynamic_range>> { - fail_fast_assert(arr.size() < PTRDIFF_MAX); + Expects(arr.size() < PTRDIFF_MAX); return {arr.data(), static_cast(arr.size())}; } @@ -1606,8 +1609,10 @@ public: constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) : m_pdata(ptr), m_bounds(std::move(bounds)) { - fail_fast_assert((m_bounds.size() > 0 && ptr != nullptr) || m_bounds.size() == 0); - fail_fast_assert(this->bounds().total_size() <= size, "Bounds cross data boundaries"); + Expects((m_bounds.size() > 0 && ptr != nullptr) || m_bounds.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void)size; } // from static array of size N @@ -1653,10 +1658,11 @@ public: template 1), typename Ret = std::enable_if_t> constexpr Ret operator[](size_type idx) const { - fail_fast_assert(idx < m_bounds.size(), "index is out of bounds of the array"); + Expects(idx < m_bounds.size()); // index is out of bounds of the array const size_type ridx = idx * m_bounds.stride(); - fail_fast_assert(ridx < m_bounds.total_size(), "index is out of bounds of the underlying data"); + // index is out of bounds of the underlying data + Expects(ridx < m_bounds.total_size()); return{ m_pdata + ridx, m_bounds.slice().total_size(), m_bounds.slice() }; } @@ -1767,7 +1773,8 @@ public: private: static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) { - fail_fast_assert(extent[Rank - 1] >= d && (extent[Rank-1] % d == 0), "The last dimension of the array needs to contain a multiple of new type elements"); + // The last dimension of the array needs to contain a multiple of new type elements + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); index_type ret = extent; ret[Rank - 1] /= d; @@ -1778,7 +1785,8 @@ private: template > static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) { - fail_fast_assert(strides[Rank - 1] == 1, "Only strided arrays with regular strides can be resized"); + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); return strides; } @@ -1786,12 +1794,16 @@ private: template 1), typename Dummy = std::enable_if_t> static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) { - fail_fast_assert(strides[Rank - 1] == 1, "Only strided arrays with regular strides can be resized"); - fail_fast_assert(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0), "The strides must have contiguous chunks of memory that can contain a multiple of new type elements"); + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); for (size_t i = Rank - 1; i > 0; --i) { - fail_fast_assert((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0), "Only strided arrays with regular strides can be resized"); + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); } index_type ret = strides / d; @@ -1818,7 +1830,9 @@ private: const Span* m_validator; void validateThis() const { - fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); + // iterator is out of range of the array + Expects(m_pdata >= m_validator->m_pdata && + m_pdata < m_validator->m_pdata + m_validator->size()); } contiguous_span_iterator (const Span* container, bool isbegin) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} @@ -1876,7 +1890,7 @@ public: } difference_type operator-(const contiguous_span_iterator& rhs) const noexcept { - fail_fast_assert(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator); return m_pdata - rhs.m_pdata; } reference operator[](difference_type n) const noexcept @@ -1885,7 +1899,7 @@ public: } bool operator==(const contiguous_span_iterator& rhs) const noexcept { - fail_fast_assert(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator); return m_pdata == rhs.m_pdata; } bool operator!=(const contiguous_span_iterator& rhs) const noexcept @@ -1894,7 +1908,7 @@ public: } bool operator<(const contiguous_span_iterator& rhs) const noexcept { - fail_fast_assert(m_validator == rhs.m_validator); + Expects(m_validator == rhs.m_validator); return m_pdata < rhs.m_pdata; } bool operator<=(const contiguous_span_iterator& rhs) const noexcept @@ -1992,7 +2006,7 @@ public: } difference_type operator-(const general_span_iterator& rhs) const noexcept { - fail_fast_assert(m_container == rhs.m_container); + Expects(m_container == rhs.m_container); return m_itr - rhs.m_itr; } value_type operator[](difference_type n) const noexcept @@ -2001,7 +2015,7 @@ public: } bool operator==(const general_span_iterator& rhs) const noexcept { - fail_fast_assert(m_container == rhs.m_container); + Expects(m_container == rhs.m_container); return m_itr == rhs.m_itr; } bool operator !=(const general_span_iterator& rhs) const noexcept @@ -2010,7 +2024,7 @@ public: } bool operator<(const general_span_iterator& rhs) const noexcept { - fail_fast_assert(m_container == rhs.m_container); + Expects(m_container == rhs.m_container); return m_itr < rhs.m_itr; } bool operator<=(const general_span_iterator& rhs) const noexcept @@ -2046,17 +2060,13 @@ general_span_iterator operator+(typename general_span_iterator::diff #undef constexpr #pragma pop_macro("constexpr") -#ifdef max -#pragma pop_macro("max") -#endif - #if _MSC_VER <= 1800 #pragma warning(pop) -#ifndef GSL_THROWS_FOR_TESTING +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION #undef noexcept #pragma pop_macro("noexcept") -#endif // GSL_THROWS_FOR_TESTING +#endif // GSL_THROW_ON_CONTRACT_VIOLATION #undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG @@ -2064,15 +2074,16 @@ general_span_iterator operator+(typename general_span_iterator::diff #endif // _MSC_VER -#if defined(GSL_THROWS_FOR_TESTING) +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #undef noexcept #ifdef _MSC_VER +#pragma warning(pop) #pragma pop_macro("noexcept") #endif -#endif // GSL_THROWS_FOR_TESTING +#endif // GSL_THROW_ON_CONTRACT_VIOLATION #endif // GSL_SPAN_H diff --git a/include/string_span.h b/include/string_span.h index b30ebf0..250c528 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -19,6 +19,7 @@ #ifndef GSL_STRING_SPAN_H #define GSL_STRING_SPAN_H +#include "gsl_assert.h" #include "span.h" #include @@ -89,7 +90,7 @@ span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIF { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; - fail_fast_assert(*cur == Sentinel); + Ensures(*cur == Sentinel); return{ seq, cur - seq }; } @@ -109,26 +110,26 @@ inline basic_string_span ensure_z(T* const & sz, std::ptrdiff_ inline basic_string_span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); + Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline basic_string_span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline basic_string_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline basic_string_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - fail_fast_assert(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5529cda..fe7a831 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ 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 -- cgit v1.2.3 From 7505111329ad32b76b479a5a29961dfb6ea59f55 Mon Sep 17 00:00:00 2001 From: Jason Horsburgh Date: Sat, 21 Nov 2015 19:13:21 +0000 Subject: Add CMake install target for header files --- CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8145d6..7397fe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,10 +2,21 @@ cmake_minimum_required(VERSION 2.8.7) project(GSL CXX) +set(GSL_HEADERS + "include/gsl.h" + "include/gsl_assert.h" + "include/span.h" + "include/string_span.h" +) + include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) +install(FILES ${GSL_HEADERS} + DESTINATION include/gsl +) + enable_testing() add_subdirectory(tests) -- cgit v1.2.3 From 38eaf9fc95149ccdff17c6c0a9f0fbed15fc41f8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 23 Nov 2015 15:07:41 -0800 Subject: Refactored headers so span can use narrow_cast etc. --- include/gsl.h | 51 +------------------- include/gsl_util.h | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 2 +- 3 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 include/gsl_util.h diff --git a/include/gsl.h b/include/gsl.h index 0575d57..ad064ba 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -20,6 +20,7 @@ #define GSL_GSL_H #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 @@ -57,56 +58,6 @@ using std::shared_ptr; template using owner = T; -// -// GSL.util: utilities -// - -// final_act allows you to ensure something gets run at the end of a scope -template -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 -final_act finally(const F &f) noexcept { return final_act(f); } - -template -final_act finally(F &&f) noexcept { return final_act(std::forward(f)); } - -// narrow_cast(): a searchable way to do narrowing casts of values -template -T narrow_cast(U u) noexcept { return static_cast(u); } - -struct narrowing_error : public std::exception {}; -// narrow() : a checked version of narrow_cast() that throws if the cast changed the value -template -T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } - -// -// at() - Bounds-checked way of accessing static arrays, std::array, std::vector -// -template -T& at(T(&arr)[N], size_t index) { Expects(index < N); return arr[index]; } - -template -T& at(std::array& arr, size_t index) { Expects(index < N); return arr[index]; } - -template -typename Cont::value_type& at(Cont& cont, size_t index) { Expects(index < cont.size()); return cont[index]; } - // // not_null diff --git a/include/gsl_util.h b/include/gsl_util.h new file mode 100644 index 0000000..b6f56ce --- /dev/null +++ b/include/gsl_util.h @@ -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 +#include +#include + +#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 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 +final_act finally(const F &f) +noexcept { return final_act(f); } + +template +final_act finally(F &&f) noexcept +{ return final_act(std::forward(f)); } + +// narrow_cast(): a searchable way to do narrowing casts of values +template +constexpr T narrow_cast(U u) noexcept +{ return static_cast(u); } + +struct narrowing_error : public std::exception {}; + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +T narrow(U u) +{ T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +constexpr T& at(T(&arr)[N], size_t index) +{ Expects(index < N); return arr[index]; } + +template +constexpr T& at(std::array& arr, size_t index) +{ Expects(index < N); return arr[index]; } + +template +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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fe7a831..a524d01 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,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 -- cgit v1.2.3 From 0cf947db7760bf5756e4cb0d47c72a257ed527c5 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 24 Nov 2015 12:49:03 -0800 Subject: Reworked span to match GSL design. --- include/gsl_assert.h | 4 +- include/gsl_util.h | 8 +- include/span.h | 1693 +++++++++++---------- tests/CMakeLists.txt | 1 + tests/span_tests.cpp | 3306 ++++++++++++++++++++---------------------- tests/strided_span_tests.cpp | 748 ++++++++++ tests/string_span_tests.cpp | 5 - 7 files changed, 3242 insertions(+), 2523 deletions(-) create mode 100644 tests/strided_span_tests.cpp diff --git a/include/gsl_assert.h b/include/gsl_assert.h index 4a40552..81cfd13 100644 --- a/include/gsl_assert.h +++ b/include/gsl_assert.h @@ -55,9 +55,9 @@ struct fail_fast : public std::runtime_error } #define Expects(cond) if (!(cond)) \ - throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ GSL_STRINGIFY(__LINE__)); + 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__)); + throw gsl::fail_fast("GSL: Postcondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)); #elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) diff --git a/include/gsl_util.h b/include/gsl_util.h index b6f56ce..e38868c 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -76,23 +76,23 @@ private: // finally() - convenience function to generate a final_act template -final_act finally(const F &f) +inline final_act finally(const F &f) noexcept { return final_act(f); } template -final_act finally(F &&f) noexcept +inline final_act finally(F &&f) noexcept { return final_act(std::forward(f)); } // narrow_cast(): a searchable way to do narrowing casts of values template -constexpr T narrow_cast(U u) noexcept +inline constexpr T narrow_cast(U u) noexcept { return static_cast(u); } struct narrowing_error : public std::exception {}; // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template -T narrow(U u) +inline T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } // diff --git a/include/span.h b/include/span.h index 0c67d22..4f0de0b 100644 --- a/include/span.h +++ b/include/span.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -19,8 +19,11 @@ #ifndef GSL_SPAN_H #define GSL_SPAN_H +#include "gsl_assert.h" +#include "gsl_util.h" #include #include +#include #include #include #include @@ -31,14 +34,12 @@ #include #include #include -#include -#include "gsl_assert.h" #ifdef _MSC_VER // turn off some warnings that are noisy about our Expects statements #pragma warning(push) -#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable : 4127) // conditional expression is constant // No MSVC does constexpr fully yet #pragma push_macro("constexpr") @@ -47,19 +48,19 @@ // VS 2013 workarounds #if _MSC_VER <= 1800 -// needed in span.h -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT -// noexcept is not understood +// noexcept is not understood #ifndef GSL_THROWS_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /* nothing */ #endif // turn off some misguided warnings #pragma warning(push) -#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable: 4512) // warns that assignment op could not be generated +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated #endif // _MSC_VER <= 1800 @@ -71,12 +72,12 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ - -#endif // GSL_THROW_ON_CONTRACT_VIOLATION +#define noexcept /* nothing */ +#endif // GSL_THROW_ON_CONTRACT_VIOLATION -namespace gsl { +namespace gsl +{ /* ** begin definitions of index and bounds @@ -89,12 +90,17 @@ namespace details static const SizeType max_value = std::numeric_limits::max(); }; + template + class are_integral : public std::integral_constant + { + }; - template - class are_integral : public std::integral_constant {}; - - template - class are_integral : public std::integral_constant::value && are_integral::value> {}; + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; } template @@ -112,26 +118,28 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; - constexpr index() noexcept - {} + constexpr index() noexcept {} - constexpr index(const value_type(&values)[Rank]) noexcept + constexpr index(const value_type (&values)[Rank]) noexcept { std::copy(values, values + Rank, elems); } #ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) : index({ static_cast(t), static_cast(ds)... }) - {} + template < + typename T, typename... Ts, + typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) + : index({narrow_cast(t), narrow_cast(ds)...}) + { + } #else - template::value>> - constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } - {} + template ::value>> + constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + { + } #endif constexpr index(const index& other) noexcept = default; @@ -157,15 +165,9 @@ public: return std::equal(elems, elems + rank, rhs.elems); } - constexpr bool operator!=(const index& rhs) const noexcept - { - return !(this == rhs); - } + constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } - constexpr index operator+() const noexcept - { - return *this; - } + constexpr index operator+() const noexcept { return *this; } constexpr index operator-() const noexcept { @@ -221,13 +223,15 @@ public: constexpr index& operator*=(value_type v) noexcept { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); return *this; } constexpr index& operator/=(value_type v) noexcept { - std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); return *this; } @@ -242,31 +246,30 @@ struct static_bounds_dynamic_range_t template ::value>> constexpr operator T() const noexcept { - return static_cast(-1); + return narrow_cast(-1); } template ::value>> - constexpr bool operator ==(T other) const noexcept + constexpr bool operator==(T other) const noexcept { - return static_cast(-1) == other; + return narrow_cast(-1) == other; } template ::value>> - constexpr bool operator !=(T other) const noexcept + constexpr bool operator!=(T other) const noexcept { - return static_cast(-1) != other; + return narrow_cast(-1) != other; } - }; template ::value>> -constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept { return right == left; } template ::value>> -constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept { return right != left; } @@ -276,8 +279,12 @@ constexpr static_bounds_dynamic_range_t dynamic_range{}; const std::ptrdiff_t dynamic_range = -1; #endif -struct generalized_mapping_tag {}; -struct contiguous_mapping_tag : generalized_mapping_tag {}; +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; namespace details { @@ -289,8 +296,9 @@ namespace details }; template - struct BoundsRanges { - using size_type = std::ptrdiff_t; + struct BoundsRanges + { + using size_type = std::ptrdiff_t; static const size_type Depth = 0; static const size_type DynamicNum = 0; static const size_type CurrentRange = 1; @@ -299,54 +307,53 @@ namespace details // TODO : following signature is for work around VS bug template BoundsRanges(const OtherRange&, bool /* firstLevel */) - {} - - BoundsRanges (const BoundsRanges&) = default; + { + } + + BoundsRanges(const BoundsRanges&) = default; BoundsRanges& operator=(const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) { } + BoundsRanges(const std::ptrdiff_t* const) {} BoundsRanges() = default; - template void serialize(T&) const - {} + { + } template size_type linearize(const T&) const - { + { return 0; } template bool contains(const T&) const { - return 0; + return false; } - size_type totalSize() const noexcept - { - return TotalSize; - } + size_type elementNum(size_t) const noexcept { return 0; } - bool operator==(const BoundsRanges&) const noexcept - { - return true; - } + size_type totalSize() const noexcept { return TotalSize; } + + bool operator==(const BoundsRanges&) const noexcept { return true; } }; template - struct BoundsRanges : BoundsRanges{ - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum + 1; static const size_type CurrentRange = dynamic_range; static const size_type TotalSize = dynamic_range; const size_type m_bound; - BoundsRanges (const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { Expects(0 <= *arr); } @@ -354,9 +361,12 @@ namespace details BoundsRanges() : m_bound(0) {} template - BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : - Base(static_cast&>(other), false), m_bound(other.totalSize()) - {} + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } template void serialize(T& arr) const @@ -367,32 +377,25 @@ namespace details template size_type linearize(const T& arr) const - { + { const size_type index = this->Base::totalSize() * arr[Dim]; Expects(index < m_bound); return index + this->Base::template linearize(arr); } - + template - size_type contains(const T & arr) const + size_type contains(const T& arr) const { const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) - return -1; + if (last == -1) return -1; const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; return cur < m_bound ? cur + last : -1; } - - size_type totalSize() const noexcept - { - return m_bound; - } - - size_type elementNum() const noexcept - { - return totalSize() / this->Base::totalSize(); - } - + + size_type totalSize() const noexcept { return m_bound; } + + size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } + size_type elementNum(size_t dim) const noexcept { if (dim > 0) @@ -401,31 +404,35 @@ namespace details return elementNum(); } - bool operator == (const BoundsRanges & rhs) const noexcept + bool operator==(const BoundsRanges& rhs) const noexcept { - return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); } }; template - struct BoundsRanges : BoundsRanges + struct BoundsRanges : BoundsRanges { - using Base = BoundsRanges ; - using size_type = std::ptrdiff_t; + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; static const size_t Depth = Base::Depth + 1; static const size_t DynamicNum = Base::DynamicNum; static const size_type CurrentRange = CurRange; - static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const BoundsRanges&) = default; - BoundsRanges (const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) { } + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} BoundsRanges() = default; template - BoundsRanges(const BoundsRanges&other, bool firstLevel = true) : Base(static_cast&>(other), false) - { - Expects((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); - (void)firstLevel; + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; } template @@ -437,31 +444,24 @@ namespace details template size_type linearize(const T& arr) const - { + { Expects(arr[Dim] < CurrentRange); // Index is out of range - return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); } template size_type contains(const T& arr) const { - if (arr[Dim] >= CurrentRange) - return -1; + if (arr[Dim] >= CurrentRange) return -1; const size_type last = this->Base::template contains(arr); - if (last == -1) - return -1; + if (last == -1) return -1; return this->Base::totalSize() * arr[Dim] + last; } - size_type totalSize() const noexcept - { - return CurrentRange * this->Base::totalSize(); - } + size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } - size_type elementNum() const noexcept - { - return CurrentRange; - } + size_type elementNum() const noexcept { return CurrentRange; } size_type elementNum(size_t dim) const noexcept { @@ -471,72 +471,59 @@ namespace details return elementNum(); } - bool operator== (const BoundsRanges& rhs) const noexcept + bool operator==(const BoundsRanges& rhs) const noexcept { - return static_cast(*this) == static_cast(rhs); + return static_cast(*this) == static_cast(rhs); } }; - template - struct BoundsRangeConvertible2; - - template > - auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant())) - {}; - - template - struct BoundsRangeConvertible2 : std::true_type {}; - - template - struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), - std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) - {}; template - struct BoundsRangeConvertible : std::true_type {}; + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; template struct TypeListIndexer { - const TypeChain & obj; - TypeListIndexer(const TypeChain & obj) :obj(obj){} - template - const TypeChain & getObj(std::true_type) + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) { - return obj; + return obj_; } - template - auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) { - return TypeListIndexer(static_cast(obj)).template get(); + return TypeListIndexer(static_cast(obj_)).template get(); } + template - auto get() -> decltype(getObj(std::integral_constant())) + auto get() -> decltype(getObj(std::integral_constant())) { return getObj(std::integral_constant()); } }; template - TypeListIndexer createTypeListIndexer(const TypeChain &obj) + TypeListIndexer createTypeListIndexer(const TypeChain& obj) { return TypeListIndexer(obj); } - template 1), typename Ret = std::enable_if_t>> + template 1), + typename Ret = std::enable_if_t>> constexpr Ret shift_left(const index& other) noexcept { Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) - { + for (size_t i = 0; i < Rank - 1; ++i) { ret[i] = other[i + 1]; } return ret; @@ -550,19 +537,17 @@ template class static_bounds { public: - static_bounds(const details::BoundsRanges&) { - } + static_bounds(const details::BoundsRanges&) {} }; template class static_bounds { - using MyRanges = details::BoundsRanges; + using MyRanges = details::BoundsRanges; MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) - {} - + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + template friend class static_bounds; @@ -581,94 +566,144 @@ public: using mapping_type = contiguous_mapping_tag; constexpr static_bounds(const static_bounds&) = default; - - template , details::BoundsRanges >::value>> + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - {} - - constexpr static_bounds(std::initializer_list il) : m_ranges((const std::ptrdiff_t*)il.begin()) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(static_cast(il.begin())) { // Size of the initializer list must match the rank of the array - Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size()); + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); } - + constexpr static_bounds() = default; constexpr static_bounds& operator=(const static_bounds& otherBounds) { - new(&m_ranges) MyRanges (otherBounds.m_ranges); + new (&m_ranges) MyRanges(otherBounds.m_ranges); return *this; } constexpr sliced_type slice() const noexcept { - return sliced_type{static_cast &>(m_ranges)}; + return sliced_type{static_cast&>(m_ranges)}; } - constexpr size_type stride() const noexcept - { - return rank > 1 ? slice().size() : 1; - } - - constexpr size_type size() const noexcept - { - return m_ranges.totalSize(); - } + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - constexpr size_type total_size() const noexcept - { - return m_ranges.totalSize(); - } - - constexpr size_type linearize(const index_type & idx) const - { - return m_ranges.linearize(idx); - } - constexpr bool contains(const index_type& idx) const noexcept { return m_ranges.contains(idx) != -1; } - + constexpr size_type operator[](size_t index) const noexcept { return m_ranges.elementNum(index); } - + template constexpr size_type extent() const noexcept { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); return details::createTypeListIndexer(m_ranges).template get().elementNum(); } - + + template + constexpr size_type extent(IntType dim) const noexcept + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + constexpr index_type index_bounds() const noexcept { size_type extents[rank] = {}; m_ranges.serialize(extents); - return{ extents }; + return {extents}; } - + template - constexpr bool operator == (const static_bounds& rhs) const noexcept + constexpr bool operator==(const static_bounds& rhs) const noexcept { return this->size() == rhs.size(); } - + template - constexpr bool operator != (const static_bounds& rhs) const noexcept + constexpr bool operator!=(const static_bounds& rhs) const noexcept { return !(*this == rhs); } - - constexpr const_iterator begin() const noexcept - { - return const_iterator(*this, index_type{}); - } - + + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } + constexpr const_iterator end() const noexcept { return const_iterator(*this, this->index_bounds()); @@ -676,19 +711,19 @@ public: }; template -class strided_bounds +class strided_bounds { template friend class strided_bounds; public: static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; using const_reference = std::add_const_t; - using size_type = value_type; + using size_type = value_type; using difference_type = value_type; - using index_type = index; + using index_type = index; using const_index_type = std::add_const_t; using iterator = bounds_iterator; using const_iterator = bounds_iterator; @@ -697,49 +732,45 @@ public: using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds &) noexcept = default; + constexpr strided_bounds(const strided_bounds&) noexcept = default; - constexpr strided_bounds & operator=(const strided_bounds &) noexcept = default; + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) : m_extents(values), m_strides(std::move(strides)) - {} - - constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept - : m_extents(extents), m_strides(strides) - {} + { + } - constexpr index_type strides() const noexcept + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), + m_strides(strides) { - return m_strides; } + constexpr index_type strides() const noexcept { return m_strides; } + constexpr size_type total_size() const noexcept { size_type ret = 0; - for (size_t i = 0; i < rank; ++i) - { + for (size_t i = 0; i < rank; ++i) { ret += (m_extents[i] - 1) * m_strides[i]; } return ret + 1; } - + constexpr size_type size() const noexcept { size_type ret = 1; - for (size_t i = 0; i < rank; ++i) - { + for (size_t i = 0; i < rank; ++i) { ret *= m_extents[i]; } return ret; } - + constexpr bool contains(const index_type& idx) const noexcept { - for (size_t i = 0; i < rank; ++i) - { - if (idx[i] < 0 || idx[i] >= m_extents[i]) - return false; + for (size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; } return true; } @@ -747,45 +778,33 @@ public: constexpr size_type linearize(const index_type& idx) const noexcept { size_type ret = 0; - for (size_t i = 0; i < rank; i++) - { + for (size_t i = 0; i < rank; i++) { Expects(idx[i] < m_extents[i]); // index is out of bounds of the array ret += idx[i] * m_strides[i]; } return ret; } - - constexpr size_type stride() const noexcept - { - return m_strides[0]; - } - + + constexpr size_type stride() const noexcept { return m_strides[0]; } + template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ details::shift_left(m_extents), details::shift_left(m_strides) }; + return {details::shift_left(m_extents), details::shift_left(m_strides)}; } - + template constexpr size_type extent() const noexcept { - static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); return m_extents[Dim]; } - - constexpr index_type index_bounds() const noexcept - { - return m_extents; - } - constexpr const_iterator begin() const noexcept - { - return const_iterator{ *this, index_type{} }; - } - - constexpr const_iterator end() const noexcept - { - return const_iterator{ *this, index_bounds() }; - } + + constexpr index_type index_bounds() const noexcept { return m_extents; } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } private: index_type m_extents; @@ -793,17 +812,23 @@ private: }; template -struct is_bounds : std::integral_constant {}; +struct is_bounds : std::integral_constant +{ +}; template -struct is_bounds> : std::integral_constant {}; +struct is_bounds> : std::integral_constant +{ +}; template -struct is_bounds> : std::integral_constant {}; +struct is_bounds> : std::integral_constant +{ +}; template -class bounds_iterator: public std::iterator +class bounds_iterator : public std::iterator { private: - using Base = std::iterator ; + using Base = std::iterator; public: static const size_t rank = IndexType::rank; @@ -815,27 +840,20 @@ public: using index_size_type = typename IndexType::value_type; template explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary(bnd.index_bounds()), curr(std::move(curr)) + : boundary(bnd.index_bounds()), + curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - constexpr reference operator*() const noexcept - { - return curr; - } + constexpr reference operator*() const noexcept { return curr; } - constexpr pointer operator->() const noexcept - { - return &curr; - } + constexpr pointer operator->() const noexcept { return &curr; } constexpr bounds_iterator& operator++() noexcept { - for (size_t i = rank; i-- > 0;) - { - if (curr[i] < boundary[i] - 1) - { + for (size_t i = rank; i-- > 0;) { + if (curr[i] < boundary[i] - 1) { curr[i]++; return *this; } @@ -855,19 +873,15 @@ public: constexpr bounds_iterator& operator--() noexcept { - if (!less(curr, boundary)) - { + if (!less(curr, boundary)) { // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) - { + for (size_t i = 0; i < rank; ++i) { curr[i] = boundary[i] - 1; } return *this; } - for (size_t i = rank; i-- > 0;) - { - if (curr[i] >= 1) - { + for (size_t i = rank; i-- > 0;) { + if (curr[i] >= 1) { curr[i]--; return *this; } @@ -875,7 +889,7 @@ public: } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" - Expects(false); + Expects(false); return *this; } @@ -888,7 +902,7 @@ public: constexpr bounds_iterator operator+(difference_type n) const noexcept { - bounds_iterator ret{ *this }; + bounds_iterator ret{*this}; return ret += n; } @@ -897,83 +911,62 @@ public: auto linear_idx = linearize(curr) + n; std::remove_const_t stride = 0; stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) - { + for (size_t i = rank - 1; i-- > 0;) { stride[i] = stride[i + 1] * boundary[i + 1]; } - for (size_t i = 0; i < rank; ++i) - { + for (size_t i = 0; i < rank; ++i) { curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } - //index is out of bounds of the array + // index is out of bounds of the array Expects(!less(curr, index_type{}) && !less(boundary, curr)); return *this; } constexpr bounds_iterator operator-(difference_type n) const noexcept { - bounds_iterator ret{ *this }; + bounds_iterator ret{*this}; return ret -= n; } - constexpr bounds_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return * this += -n; } constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr) - linearize(rhs.curr); } - constexpr value_type operator[](difference_type n) const noexcept - { - return *(*this + n); - } + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept - { - return !(*this == rhs); - } + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } constexpr bool operator<(const bounds_iterator& rhs) const noexcept { return less(curr, rhs.curr); } - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept - { - return !(rhs < *this); - } + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - constexpr bool operator>(const bounds_iterator& rhs) const noexcept - { - return rhs < *this; - } + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept - { - return !(rhs > *this); - } + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } void swap(bounds_iterator& rhs) noexcept { std::swap(boundary, rhs.boundary); std::swap(curr, rhs.curr); } + private: constexpr bool less(index_type& one, index_type& other) const noexcept { - for (size_t i = 0; i < rank; ++i) - { - if (one[i] < other[i]) - return true; + for (size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; } return false; } @@ -984,19 +977,16 @@ private: // Check if past-the-end index_size_type multiplier = 1; index_size_type res = 0; - if (!less(idx, boundary)) - { + if (!less(idx, boundary)) { res = 1; - for (size_t i = rank; i-- > 0;) - { + for (size_t i = rank; i-- > 0;) { res += (idx[i] - 1) * multiplier; multiplier *= boundary[i]; } } else { - for (size_t i = rank; i-- > 0;) - { + for (size_t i = rank; i-- > 0;) { res += idx[i] * multiplier; multiplier *= boundary[i]; } @@ -1009,54 +999,63 @@ private: }; template -bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) noexcept +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) noexcept { return rhs + n; } -// -// begin definitions of basic_span -// namespace details { template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept { return bnd.strides(); } // Make a stride vector from bounds, assuming contiguous memory. template - constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); typename Bounds::size_type stride[Bounds::rank] = {}; stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) - { + for (size_t i = 1; i < Bounds::rank; ++i) { stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; } - return{ stride }; + return {stride}; } template - void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) - { - static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); - static_assert(std::is_same::value, "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + static_assert(is_bounds::value && is_bounds::value, + "The src type and dest type must be bounds"); + static_assert(std::is_same::value, + "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || + BoundsSrc::static_size == dynamic_range || + BoundsDest::static_size == BoundsSrc::static_size, + "The source bounds must have same size as dest bounds"); Expects(src.size() == dest.size()); } - } // namespace details template class contiguous_span_iterator; template class general_span_iterator; -enum class byte : std::uint8_t {}; +enum class byte : std::uint8_t +{ +}; template struct dim @@ -1071,7 +1070,8 @@ struct dim dim(std::ptrdiff_t size) : dvalue(size) {} }; -template +template class span; template @@ -1087,14 +1087,15 @@ namespace details }; template - struct SpanTypeTraits::type> + struct SpanTypeTraits::type> { using value_type = typename Traits::span_traits::value_type; using size_type = typename Traits::span_traits::size_type; }; template - struct SpanArrayTraits { + struct SpanArrayTraits + { using type = span; using value_type = T; using bounds_type = static_bounds; @@ -1102,46 +1103,53 @@ namespace details using reference = T&; }; template - struct SpanArrayTraits : SpanArrayTraits {}; + struct SpanArrayTraits : SpanArrayTraits + { + }; template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size { - Expects(totalSize <= PTRDIFF_MAX); + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); return BoundsType{totalSize}; } template BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size { - Expects(BoundsType::static_size == totalSize); + Expects(BoundsType::static_size <= totalSize); return {}; } template BoundsType newBoundsHelper(std::ptrdiff_t totalSize) { static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl(totalSize, std::integral_constant()); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); } - - struct Sep{}; - + + struct Sep + { + }; + template T static_as_span_helper(Sep, Args... args) { - return T{static_cast(args)...}; + return T{narrow_cast(args)...}; } template - std::enable_if_t>::value && !std::is_same::value, T> static_as_span_helper(Arg, Args... args) + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_span_helper(Arg, Args... args) { return static_as_span_helper(args...); } template - T static_as_span_helper(dim val, Args ... args) + T static_as_span_helper(dim val, Args... args) { return static_as_span_helper(args..., val.dvalue); } - template + template struct static_as_span_static_bounds_helper { using type = static_bounds<(Dimensions::value)...>; @@ -1149,32 +1157,36 @@ namespace details template struct is_span_oracle : std::false_type - {}; + { + }; template struct is_span_oracle> : std::true_type - {}; - + { + }; + template struct is_span_oracle> : std::true_type - {}; - + { + }; + template struct is_span : is_span_oracle> - {}; - + { + }; } - template class span { - template + // TODO do we still need this? + template friend class span; public: using bounds_type = static_bounds; - static const size_t rank = bounds_type::rank; + static const size_t Rank = bounds_type::rank; using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; using value_type = ValueType; @@ -1186,405 +1198,549 @@ public: using const_iterator = contiguous_span_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - using sliced_type = std::conditional_t>; + using sliced_type = + std::conditional_t>; private: - pointer m_pdata; - bounds_type m_bounds; + pointer data_; + bounds_type bounds_; friend iterator; friend const_iterator; public: - - constexpr span(pointer data, bounds_type bounds) noexcept - : m_pdata(data), m_bounds(std::move(bounds)) + // default constructor - same as constructing from nullptr_t + constexpr span() noexcept : span(nullptr, bounds_type{}) { - Expects((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of span only possible " + "for dynamic or fixed, zero-length spans."); } - constexpr span(pointer ptr, size_type size) noexcept - : span(ptr, bounds_type{ size }) - {} - - constexpr span(std::nullptr_t) noexcept - : span(nullptr, bounds_type{}) - {} + // construct from nullptr - get an empty span + constexpr span(std::nullptr_t) noexcept : span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of span only possible " + "for dynamic or fixed, zero-length spans."); + } - constexpr span(std::nullptr_t, size_type size) noexcept - : span(nullptr, bounds_type{}) + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr span(std::nullptr_t, IntType size) noexcept : span(nullptr, bounds_type{}) { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of span only possible " + "for dynamic or fixed, zero-length spans."); Expects(size == 0); } - // default - template > - constexpr span() noexcept - : span(nullptr, bounds_type()) - {} - - // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) - template , - typename Dummy = std::enable_if_t>::value> - /*typename Dummy = std::enable_if_t::value>*/ - > - constexpr span(T* const& data, size_type size) : span(reinterpret_cast(data), typename Helper::bounds_type{size}) - {} - - // from n-dimensions static array - template , - typename = std::enable_if_t::value> - > - constexpr span (T (&arr)[N]) : span(reinterpret_cast(arr), typename Helper::bounds_type()) - {} - - // from n-dimensions static array with size - template , - typename = std::enable_if_t::value> - > - constexpr span(T(&arr)[N], size_type size) : span(arr, typename Helper::bounds_type{size}) - { - Expects(size <= N); - } - - // from std array - template , bounds_type>::value> - > - constexpr span (std::array, N> & arr) : span(arr.data(), static_bounds()) - {} - - template , bounds_type>::value - && std::is_const::value> - > - constexpr span (const std::array, N> & arr) : span(arr.data(), static_bounds()) - {} - - // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity - template ::value - && details::LessThan::value> - > // remove literal 0 case - constexpr span (pointer begin, Ptr end) : span(begin, details::newBoundsHelper(static_cast(end) - begin)) - {} - - // from containers. It must has .size() and .data() two function signatures - template ::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> - > - constexpr span (Cont& cont) : span(static_cast(cont.data()), details::newBoundsHelper(static_cast(cont.size()))) - {} + // construct from a single element + constexpr span(reference data) noexcept : span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } - constexpr span(const span &) = default; + // prevent constructing from temporaries for single-elements + constexpr span(value_type&&) = delete; - // convertible - template , - typename Dummy = std::enable_if_t::value && std::is_convertible::value> - > - constexpr span(const span& other) noexcept - : m_pdata(other.m_pdata), m_bounds(other.m_bounds) - {} + // construct from pointer + length + constexpr span(pointer ptr, size_type size) noexcept : span(ptr, bounds_type{size}) {} - // reshape - // DimCount here is a workaround for a bug in MSVC 2015 - template 0), typename Dummy = std::enable_if_t> - constexpr span as_span(Dimensions2... dims) + // construct from pointer + length - multidimensional + constexpr span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) { - using BoundsType = typename span::bounds_type; - auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(this->bounds(), tobounds); - return {this->data(), tobounds}; + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); } - // to bytes array - template >::value> - auto as_bytes() const noexcept -> span + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr span(pointer begin, Ptr end) + : span(begin, details::newBoundsHelper(static_cast(end) - begin)) { - static_assert(Enabled, "The value_type of span must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); } - template >::value> - auto as_writeable_bytes() const noexcept -> span + // construct from n-dimensions static array + template > + constexpr span(T (&arr)[N]) + : span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) { - static_assert(Enabled, "The value_type of span must be standarded layout"); - return { reinterpret_cast(this->data()), this->bytes() }; + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible::value, + "Cannot construct a span from an array with fewer elements."); } - // from bytes array - template::value, typename = std::enable_if_t> - constexpr auto as_span() const noexcept -> span(static_cast(bounds_type::static_size) / sizeof(U)) : dynamic_range)> + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr span(T* const& data, size_type size) + : span(reinterpret_cast(data), typename Helper::bounds_type{size}) { - static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - Expects((this->bytes() % sizeof(U)) == 0 && (this->bytes() / sizeof(U)) < PTRDIFF_MAX); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target span type."); } - template::value, typename = std::enable_if_t> - constexpr auto as_span() const noexcept -> span(static_cast(bounds_type::static_size) / sizeof(U)) : dynamic_range)> + // construct from std::array + template + constexpr span(std::array& arr) : span(arr.data(), bounds_type{static_bounds{}}) { - static_assert(std::is_standard_layout::value && (bounds_type::static_size == dynamic_range || bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be standard layout and its size must match the byte array size"); - Expects((this->bytes() % sizeof(U)) == 0); - return { reinterpret_cast(this->data()), this->bytes() / static_cast(sizeof(U)) }; + static_assert( + std::is_convertible(*) []>::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a span from a std::array of smaller size."); } - // section on linear space - template - constexpr span first() const noexcept + // construct from const std::array + template + constexpr span(const std::array, N>& arr) + : span(arr.data(), static_bounds()) { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return { this->data(), Count }; + static_assert(std::is_convertible>::value, + "Cannot convert from source type to target span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a span from a std::array of smaller size."); } - constexpr span first(size_type count) const noexcept - { - Expects(count <= this->size()); - return { this->data(), count }; - } + // prevent constructing from temporary std::array + template + constexpr span(std::array&& arr) = delete; - template - constexpr span last() const noexcept + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr span(Cont& cont) + : span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) { - static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return { this->data() + this->size() - Count, Count }; } - constexpr span last(size_type count) const noexcept + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr span(Cont&& cont) = delete; + + // construct from a convertible span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr span(span other) noexcept : data_(other.data_), + bounds_(other.bounds_) { - Expects(count <= this->size()); - return { this->data() + this->size() - count, count }; } - template - constexpr span sub() const noexcept +// trivial copy and move +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr span(span&&) = default; +#endif + constexpr span(const span&) = default; + +// trivial assignment +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr span& operator=(span&&) = default; +#endif + constexpr span& operator=(const span&) = default; + + // first() - extract the first Count elements into a new span + template + constexpr span first() const noexcept { - static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset <= bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); - Expects(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset <= this->size()) && Offset + Count <= this->size())); - return { this->data() + Offset, Count }; + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; } - constexpr span sub(size_type offset, size_type count = dynamic_range) const noexcept + // first() - extract the first count elements into a new span + constexpr span first(size_type count) const noexcept { - Expects((offset == 0 || offset <= this->size()) && (count == dynamic_range || (offset + count) <= this->size())); - return { this->data() + offset, count == dynamic_range ? this->length() - offset : count }; + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; } - // size - constexpr size_type length() const noexcept + // last() - extract the last Count elements into a new span + template + constexpr span last() const noexcept { - return this->size(); + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; } - constexpr size_type used_length() const noexcept + // last() - extract the last count elements into a new span + constexpr span last(size_type count) const noexcept { - return length(); + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; } - constexpr size_type bytes() const noexcept + // subspan() - create a subview of Count elements starting at Offset + template + constexpr span subspan() const noexcept { - return sizeof(value_type) * this->size(); + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; } - constexpr size_type used_bytes() const noexcept + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr span subspan(size_type offset, + size_type count = dynamic_range) const noexcept { - return bytes(); + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; } - // section - constexpr strided_span section(index_type origin, index_type extents) const noexcept + // section - creates a non-contiguous, strided span from a contiguous one + constexpr strided_span section(index_type origin, index_type extents) const + noexcept { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return{ &this->operator[](origin), size, strided_bounds {extents, details::make_stride(bounds())} }; - } - - constexpr reference operator[](const index_type& idx) const noexcept - { - return m_pdata[m_bounds.linearize(idx)]; + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const noexcept - { - Expects(idx < m_bounds.size()); // index is out of bounds of the array - const size_type ridx = idx * m_bounds.stride(); - // index is out of bounds of the underlying data - Expects(ridx < m_bounds.total_size()); - return Ret{ m_pdata + ridx, m_bounds.slice() }; - } + // length of the span in elements + constexpr size_type size() const noexcept { return bounds_.size(); } - constexpr bounds_type bounds() const noexcept - { - return m_bounds; - } + // length of the span in elements + constexpr size_type length() const noexcept { return this->size(); } + + // length of the span in bytes + constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } + + // length of the span in bytes + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } + + constexpr bool empty() const noexcept { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } template constexpr size_type extent() const noexcept { - static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); - return m_bounds.template extent(); + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); } - constexpr size_type size() const noexcept + template + constexpr size_type extent(IntType dim) const noexcept { - return m_bounds.size(); + return bounds_.extent(dim); } - constexpr pointer data() const noexcept + constexpr bounds_type bounds() const noexcept { return bounds_; } + + constexpr pointer data() const noexcept { return data_; } + + template + constexpr reference operator()(FirstIndex index) { - return m_pdata; + return this->operator[](narrow_cast(index)); } - constexpr explicit operator bool() const noexcept + template + constexpr reference operator()(FirstIndex index, OtherIndices... indices) { - return m_pdata != nullptr; + index_type idx = {narrow_cast(index), + narrow_cast(indices...)}; + return this->operator[](idx); } - constexpr iterator begin() const noexcept + constexpr reference operator[](const index_type& idx) const noexcept { - return iterator{ this, true }; + return data_[bounds_.linearize(idx)]; } - constexpr iterator end() const noexcept + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const noexcept { - return iterator{ this, false }; + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; } + constexpr iterator begin() const noexcept { return iterator{this, true}; } + + constexpr iterator end() const noexcept { return iterator{this, false}; } + constexpr const_iterator cbegin() const noexcept { - return const_iterator{ reinterpret_cast(this), true }; + return const_iterator{reinterpret_cast(this), true}; } constexpr const_iterator cend() const noexcept { - return const_iterator{ reinterpret_cast(this), false }; + return const_iterator{reinterpret_cast(this), false}; } - constexpr reverse_iterator rbegin() const noexcept - { - return reverse_iterator{ end() }; - } + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept - { - return reverse_iterator{ begin() }; - } + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } constexpr const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator{ cend() }; + return const_reverse_iterator{cend()}; } constexpr const_reverse_iterator crend() const noexcept { - return const_reverse_iterator{ cbegin() }; + return const_reverse_iterator{cbegin()}; } - template , std::remove_cv_t>::value>> - constexpr bool operator== (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator==(const span& other) const noexcept { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); } - template , std::remove_cv_t>::value>> - constexpr bool operator!= (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const span& other) const noexcept { return !(*this == other); } - template , std::remove_cv_t>::value>> - constexpr bool operator< (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator<(const span& other) const noexcept { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } - template , std::remove_cv_t>::value>> - constexpr bool operator<= (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const span& other) const noexcept { return !(other < *this); } - template , std::remove_cv_t>::value>> - constexpr bool operator> (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator>(const span& other) const noexcept { return (other < *this); } - template , std::remove_cv_t>::value>> - constexpr bool operator>= (const span & other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const span& other) const noexcept { return !(*this < other); } }; +// +// Free functions for manipulating spans +// + +// reshape a span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr span as_span(SpanType s, + Dimensions2... dims) +{ + static_assert(details::is_span::value, + "Variadic as_span() is for reshaping existing spans."); + using BoundsType = + typename span::bounds_type; + auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a span to a span +template +span as_bytes(span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a span to a span (a writeable byte span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +span as_writeable_bytes(span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a span to a span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(span s) noexcept + -> span( + span::bounds_type::static_size != dynamic_range + ? (static_cast( + span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a span to a span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(span s) noexcept -> span< + U, narrow_cast( + span::bounds_type::static_size != dynamic_range + ? static_cast(span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + template -constexpr auto as_span(T* const& ptr, dim... args) -> span, Dimensions...> +constexpr auto as_span(T* const& ptr, dim... args) + -> span, Dimensions...> { - return {reinterpret_cast*>(ptr), details::static_as_span_helper>(args..., details::Sep{})}; + return {reinterpret_cast*>(ptr), + details::static_as_span_helper>(args..., details::Sep{})}; } template -constexpr auto as_span (T* arr, std::ptrdiff_t len) -> typename details::SpanArrayTraits::type +constexpr auto as_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type { return {reinterpret_cast*>(arr), len}; } template -constexpr auto as_span (T (&arr)[N]) -> typename details::SpanArrayTraits::type +constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type { return {arr}; } template -constexpr span as_span(const std::array &arr) +constexpr span as_span(const std::array& arr) { return {arr}; } template -constexpr span as_span(const std::array &&) = delete; +constexpr span as_span(const std::array&&) = delete; template -constexpr span as_span(std::array &arr) +constexpr span as_span(std::array& arr) { return {arr}; } template -constexpr span as_span(T *begin, T *end) +constexpr span as_span(T* begin, T* end) { return {begin, end}; } template -constexpr auto as_span(Cont &arr) -> std::enable_if_t>::value, +constexpr auto as_span(Cont& arr) -> std::enable_if_t< + !details::is_span>::value, span, dynamic_range>> { Expects(arr.size() < PTRDIFF_MAX); - return {arr.data(), static_cast(arr.size())}; + return {arr.data(), narrow_cast(arr.size())}; } template -constexpr auto as_span(Cont &&arr) -> std::enable_if_t>::value, +constexpr auto as_span(Cont&& arr) -> std::enable_if_t< + !details::is_span>::value, span, dynamic_range>> = delete; // from basic_string which doesn't have nonconst .data() member like other contiguous containers template -constexpr auto as_span(std::basic_string &str) -> span +constexpr auto as_span(std::basic_string& str) + -> span { Expects(str.size() < PTRDIFF_MAX); - return {&str[0], static_cast(str.size())}; + return {&str[0], narrow_cast(str.size())}; } +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. template class strided_span { @@ -1601,11 +1757,12 @@ public: using const_iterator = general_span_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - using sliced_type = std::conditional_t>; + using sliced_type = + std::conditional_t>; private: - pointer m_pdata; - bounds_type m_bounds; + pointer data_; + bounds_type bounds_; friend iterator; friend const_iterator; @@ -1615,169 +1772,163 @@ private: public: // from raw data constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) - : m_pdata(ptr), m_bounds(std::move(bounds)) + : data_(ptr), bounds_(std::move(bounds)) { - Expects((m_bounds.size() > 0 && ptr != nullptr) || m_bounds.size() == 0); + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); // Bounds cross data boundaries Expects(this->bounds().total_size() <= size); - (void)size; + (void) size; } // from static array of size N - template - constexpr strided_span(value_type(&values)[N], bounds_type bounds) : strided_span(values, N, std::move(bounds)) - {} + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } // from array view template ::value, - typename Dummy = std::enable_if_t - > - constexpr strided_span(span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - {} - + bool Enabled1 = (sizeof...(Dimensions) == Rank), + bool Enabled2 = std::is_convertible::value, + typename Dummy = std::enable_if_t> + constexpr strided_span(span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + // convertible - template ::value> - > + template ::value>> constexpr strided_span(const strided_span& other) - : m_pdata(other.m_pdata), m_bounds(other.m_bounds) - {} + : data_(other.data_), bounds_(other.bounds_) + { + } // convert from bytes template - constexpr strided_span::value, OtherValueType>::type, Rank> as_strided_span() const + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && (sizeof(OtherValueType) % sizeof(value_type) == 0), "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = static_cast(sizeof(OtherValueType) / sizeof(value_type)); + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); size_type size = this->bounds().total_size() / d; - return{ (OtherValueType*)this->data(), size, bounds_type{ resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)} }; + return {(OtherValueType*) this->data(), size, + bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; } constexpr strided_span section(index_type origin, index_type extents) const { size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return { &this->operator[](origin), size, bounds_type {extents, details::make_stride(bounds())}}; + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; } constexpr reference operator[](const index_type& idx) const { - return m_pdata[m_bounds.linearize(idx)]; + return data_[bounds_.linearize(idx)]; } template 1), typename Ret = std::enable_if_t> constexpr Ret operator[](size_type idx) const { - Expects(idx < m_bounds.size()); // index is out of bounds of the array - const size_type ridx = idx * m_bounds.stride(); + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); // index is out of bounds of the underlying data - Expects(ridx < m_bounds.total_size()); - return{ m_pdata + ridx, m_bounds.slice().total_size(), m_bounds.slice() }; + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; } - constexpr bounds_type bounds() const noexcept - { - return m_bounds; - } + constexpr bounds_type bounds() const noexcept { return bounds_; } template constexpr size_type extent() const noexcept { - static_assert(Dim < Rank, "dimension should be less than Rank (dimension count starts from 0)"); - return m_bounds.template extent(); + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); } - constexpr size_type size() const noexcept - { - return m_bounds.size(); - } + constexpr size_type size() const noexcept { return bounds_.size(); } - constexpr pointer data() const noexcept - { - return m_pdata; - } + constexpr pointer data() const noexcept { return data_; } - constexpr explicit operator bool() const noexcept - { - return m_pdata != nullptr; - } + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } - constexpr iterator begin() const - { - return iterator{ this, true }; - } + constexpr iterator begin() const { return iterator{this, true}; } - constexpr iterator end() const - { - return iterator{ this, false }; - } + constexpr iterator end() const { return iterator{this, false}; } constexpr const_iterator cbegin() const { - return const_iterator{ reinterpret_cast(this), true }; + return const_iterator{reinterpret_cast(this), true}; } constexpr const_iterator cend() const { - return const_iterator{ reinterpret_cast(this), false }; + return const_iterator{reinterpret_cast(this), false}; } - constexpr reverse_iterator rbegin() const - { - return reverse_iterator{ end() }; - } + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const - { - return reverse_iterator{ begin() }; - } + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const - { - return const_reverse_iterator{ cend() }; - } + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const - { - return const_reverse_iterator{ cbegin() }; - } + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } - template , std::remove_cv_t>::value>> - constexpr bool operator== (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const noexcept { - return m_bounds.size() == other.m_bounds.size() && - (m_pdata == other.m_pdata || std::equal(this->begin(), this->end(), other.begin())); + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); } - template , std::remove_cv_t>::value>> - constexpr bool operator!= (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const noexcept { return !(*this == other); } - template , std::remove_cv_t>::value>> - constexpr bool operator< (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const noexcept { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } - template , std::remove_cv_t>::value>> - constexpr bool operator<= (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const noexcept { return !(other < *this); } - template , std::remove_cv_t>::value>> - constexpr bool operator> (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const noexcept { return (other < *this); } - template , std::remove_cv_t>::value>> - constexpr bool operator>= (const strided_span& other) const noexcept + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const noexcept { return !(*this < other); } @@ -1795,7 +1946,7 @@ private: } template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t , void * = 0) + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) { // Only strided arrays with regular strides can be resized Expects(strides[Rank - 1] == 1); @@ -1812,8 +1963,7 @@ private: // memory that can contain a multiple of new type elements Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - for (size_t i = Rank - 1; i > 0; --i) - { + for (size_t i = Rank - 1; i > 0; --i) { // Only strided arrays with regular strides can be resized Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); } @@ -1826,9 +1976,11 @@ private: }; template -class contiguous_span_iterator : public std::iterator +class contiguous_span_iterator + : public std::iterator { using Base = std::iterator; + public: using typename Base::reference; using typename Base::pointer; @@ -1838,33 +1990,36 @@ private: template friend class span; - pointer m_pdata; + pointer data_; const Span* m_validator; void validateThis() const { // iterator is out of range of the array - Expects(m_pdata >= m_validator->m_pdata && - m_pdata < m_validator->m_pdata + m_validator->size()); + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); } - contiguous_span_iterator (const Span* container, bool isbegin) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + public: reference operator*() const noexcept { validateThis(); - return *m_pdata; + return *data_; } pointer operator->() const noexcept { validateThis(); - return m_pdata; + return data_; } contiguous_span_iterator& operator++() noexcept { - ++m_pdata; + ++data_; return *this; } - contiguous_span_iterator operator++(int)noexcept + contiguous_span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); @@ -1872,10 +2027,10 @@ public: } contiguous_span_iterator& operator--() noexcept { - --m_pdata; + --data_; return *this; } - contiguous_span_iterator operator--(int)noexcept + contiguous_span_iterator operator--(int) noexcept { auto ret = *this; --(*this); @@ -1883,104 +2038,87 @@ public: } contiguous_span_iterator operator+(difference_type n) const noexcept { - contiguous_span_iterator ret{ *this }; + contiguous_span_iterator ret{*this}; return ret += n; } contiguous_span_iterator& operator+=(difference_type n) noexcept { - m_pdata += n; + data_ += n; return *this; } contiguous_span_iterator operator-(difference_type n) const noexcept { - contiguous_span_iterator ret{ *this }; + contiguous_span_iterator ret{*this}; return ret -= n; } - contiguous_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } difference_type operator-(const contiguous_span_iterator& rhs) const noexcept { Expects(m_validator == rhs.m_validator); - return m_pdata - rhs.m_pdata; - } - reference operator[](difference_type n) const noexcept - { - return *(*this + n); + return data_ - rhs.data_; } + reference operator[](difference_type n) const noexcept { return *(*this + n); } bool operator==(const contiguous_span_iterator& rhs) const noexcept { Expects(m_validator == rhs.m_validator); - return m_pdata == rhs.m_pdata; - } - bool operator!=(const contiguous_span_iterator& rhs) const noexcept - { - return !(*this == rhs); + return data_ == rhs.data_; } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } bool operator<(const contiguous_span_iterator& rhs) const noexcept { Expects(m_validator == rhs.m_validator); - return m_pdata < rhs.m_pdata; - } - bool operator<=(const contiguous_span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const contiguous_span_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const contiguous_span_iterator& rhs) const noexcept - { - return !(rhs > *this); + return data_ < rhs.data_; } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } void swap(contiguous_span_iterator& rhs) noexcept { - std::swap(m_pdata, rhs.m_pdata); + std::swap(data_, rhs.data_); std::swap(m_validator, rhs.m_validator); } }; template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, const contiguous_span_iterator& rhs) noexcept +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) noexcept { return rhs + n; } template -class general_span_iterator : public std::iterator +class general_span_iterator + : public std::iterator { using Base = std::iterator; + public: using typename Base::reference; using typename Base::pointer; using typename Base::difference_type; using typename Base::value_type; + private: template friend class strided_span; - + const Span* m_container; typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span* container, bool isbegin) : - m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - {} -public: - reference operator*() noexcept - { - return (*m_container)[*m_itr]; - } - pointer operator->() noexcept + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) { - return &(*m_container)[*m_itr]; } + +public: + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } general_span_iterator& operator++() noexcept { ++m_itr; return *this; } - general_span_iterator operator++(int)noexcept + general_span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); @@ -1991,7 +2129,7 @@ public: --m_itr; return *this; } - general_span_iterator operator--(int)noexcept + general_span_iterator operator--(int) noexcept { auto ret = *this; --(*this); @@ -1999,7 +2137,7 @@ public: } general_span_iterator operator+(difference_type n) const noexcept { - general_span_iterator ret{ *this }; + general_span_iterator ret{*this}; return ret += n; } general_span_iterator& operator+=(difference_type n) noexcept @@ -2009,13 +2147,10 @@ public: } general_span_iterator operator-(difference_type n) const noexcept { - general_span_iterator ret{ *this }; + general_span_iterator ret{*this}; return ret -= n; } - general_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + general_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } difference_type operator-(const general_span_iterator& rhs) const noexcept { Expects(m_container == rhs.m_container); @@ -2023,34 +2158,23 @@ public: } value_type operator[](difference_type n) const noexcept { - return (*m_container)[m_itr[n]];; + return (*m_container)[m_itr[n]]; + ; } bool operator==(const general_span_iterator& rhs) const noexcept { Expects(m_container == rhs.m_container); return m_itr == rhs.m_itr; } - bool operator !=(const general_span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } + bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } bool operator<(const general_span_iterator& rhs) const noexcept { Expects(m_container == rhs.m_container); return m_itr < rhs.m_itr; } - bool operator<=(const general_span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - bool operator>(const general_span_iterator& rhs) const noexcept - { - return rhs < *this; - } - bool operator>=(const general_span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } + bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } void swap(general_span_iterator& rhs) noexcept { std::swap(m_itr, rhs.m_itr); @@ -2059,14 +2183,14 @@ public: }; template -general_span_iterator operator+(typename general_span_iterator::difference_type n, const general_span_iterator& rhs) noexcept +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) noexcept { return rhs + n; } } // namespace gsl - #ifdef _MSC_VER #undef constexpr @@ -2080,13 +2204,13 @@ general_span_iterator operator+(typename general_span_iterator::diff #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION -#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG #endif // _MSC_VER <= 1800 #endif // _MSC_VER -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #undef noexcept @@ -2095,7 +2219,6 @@ general_span_iterator operator+(typename general_span_iterator::diff #pragma pop_macro("noexcept") #endif -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - +#endif // GSL_THROW_ON_CONTRACT_VIOLATION #endif // GSL_SPAN_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a524d01..7990ec3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -45,6 +45,7 @@ function(add_gsl_test name) endfunction() 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) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index e5078af..8737db9 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -1,1827 +1,1679 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include #include +#include +#include +#include +#include #include #include -#include -#include using namespace std; using namespace gsl; -namespace +namespace +{ +struct BaseClass { - struct BaseClass {}; - struct DerivedClass : BaseClass {}; +}; +struct DerivedClass : BaseClass +{ +}; } SUITE(span_tests) { - TEST(basics) - { - auto ptr = as_span(new int[10], 10); - fill(ptr.begin(), ptr.end(), 99); - for (int num : ptr) - { - CHECK(num == 99); - } - delete[] ptr.data(); + TEST(default_constructor) + { + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); - static_bounds<4, dynamic_range, 2> bounds{ 3 }; - -#ifdef CONFIRM_COMPILATION_ERRORS - span av(nullptr, bounds); - av.extent(); - av.extent<2>(); - av[8][4][3]; -#endif - } + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } - TEST (span_convertible) - { + { #ifdef CONFIRM_COMPILATION_ERRORS - span av1(nullptr, b1); + span s; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif - - auto f = [&]() { span av1(nullptr); }; - CHECK_THROW(f(), fail_fast); - - span av1(nullptr); + } + { + span s{}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_constructor) + { + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - static_bounds b12(b11); - b12 = b11; - b11 = b12; - - span av1 = nullptr; - span av2(av1); - span av2(av1); + span s = nullptr; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_length_constructor) + { + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } - span avd; + { #ifdef CONFIRM_COMPILATION_ERRORS - span avb = avd; + span s{nullptr, 0}; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif - span avcd = avd; - (void)avcd; - } - - TEST(boundary_checks) - { - int arr[10][2]; - auto av = as_span(arr); - - fill(begin(av), end(av), 0); + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_element_constructor) + { + int i = 5; - av[2][0] = 1; - av[1][1] = 3; - - // out of bounds - CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[{1, 3}] = 3), fail_fast); + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + + span cs = i; + CHECK(cs.length() == 1 && cs.data() == &i); + CHECK(cs[0] == 5); + } - CHECK_THROW(av[10][2], fail_fast); - CHECK_THROW((av[{10,2}]), fail_fast); - } - - void overloaded_func(span exp, int expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - void overloaded_func(span exp, char expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - void fixed_func(span exp, int expected_value) { - for (auto val : exp) - { - CHECK(val == expected_value); - } - } - - TEST(span_parameter_test) - { - auto data = new int[4][3][5]; - - auto av = as_span(data, 4); - - CHECK(av.size() == 60); - - fill(av.begin(), av.end(), 34); - - int count = 0; - for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); - CHECK(count == 34 * 60); - overloaded_func(av, 34); - - overloaded_func(av.as_span(dim<>(4), dim<>(3), dim<>(5)), 34); - - //fixed_func(av, 34); - delete[] data; - } - - - TEST(md_access) - { - auto width = 5, height = 20; - - auto imgSize = width * height; - auto image_ptr = new int[imgSize][3]; - - // size check will be done - auto image_view = as_span(image_ptr, imgSize).as_span(dim<>(height), dim<>(width), dim<3>()); - - iota(image_view.begin(), image_view.end(), 1); - - int expected = 0; - for (auto i = 0; i < height; i++) - { - for (auto j = 0; j < width; j++) - { - CHECK(expected + 1 == image_view[i][j][0]); - CHECK(expected + 2 == image_view[i][j][1]); - CHECK(expected + 3 == image_view[i][j][2]); - - auto val = image_view[{i, j, 0}]; - CHECK(expected + 1 == val); - val = image_view[{i, j, 1}]; - CHECK(expected + 2 == val); - val = image_view[{i, j, 2}]; - CHECK(expected + 3 == val); - - expected += 3; - } - } - } - - TEST(span_factory_test) - { - { - int * arr = new int[150]; - - auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); - - fill(av.begin(), av.end(), 24); - overloaded_func(av, 24); - - delete[] arr; - - - array stdarr{ 0 }; - auto av2 = as_span(stdarr); - overloaded_func(av2.as_span(dim<>(1), dim<3>(), dim<5>()), 0); - - - string str = "ttttttttttttttt"; // size = 15 - auto t = str.data(); - (void)t; - auto av3 = as_span(str); - overloaded_func(av3.as_span(dim<>(1), dim<3>(), dim<5>()), 't'); - } - - { - string str; - span strspan = as_span(str); - (void)strspan; - const string cstr; - span cstrspan = as_span(cstr); - (void)cstrspan; - } - - { - int a[3][4][5]; - auto av = as_span(a); - const int (*b)[4][5]; - b = a; - auto bv = as_span(b, 3); - - CHECK(av == bv); - - const std::array arr = {0.0, 0.0, 0.0}; - auto cv = as_span(arr); - (void)cv; - - vector vec(3); - auto dv = as_span(vec); - (void)dv; - + { #ifdef CONFIRM_COMPILATION_ERRORS - auto dv2 = as_span(std::move(vec)); + const j = 1; + span s = j; #endif - } - } - - template void fn(const Bounds&) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } - TEST (span_reshape_test) - { - int a[3][4][5]; - auto av = as_span(a); - fn(av.bounds()); - auto av2 = av.as_span(dim<60>()); - auto av3 = av2.as_span(dim<3>(), dim<4>(), dim<5>()); - auto av4 = av3.as_span(dim<4>(), dim<>(3), dim<5>()); - auto av5 = av4.as_span(dim<3>(), dim<4>(), dim<5>()); - auto av6 = av5.as_span(dim<12>(), dim<>(5)); - - fill(av6.begin(), av6.end(), 1); - - auto av7 = av6.as_bytes(); - - auto av8 = av7.as_span(); - - CHECK(av8.size() == av6.size()); - for (auto i = 0; i < av8.size(); i++) - { - CHECK(av8[i] == 1); - } + } + { #ifdef CONFIRM_COMPILATION_ERRORS - struct Foo {char c[11];}; - auto av9 = av7.as_span(); -#endif - } - - - 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 data(5 * 10); - std::iota(begin(data), end(data), 0); - const span av = as_span(data).as_span(dim<5>(), dim<10>()); - - strided_span 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 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 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 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 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 src = arr; - - strided_span 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 sav_c{ {src}, {2, 1} }; - strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; -#else - strided_span sav_c{ span{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 sav_v{ src, {2, 1} }; -#else - strided_span sav_v{ span{src}, strided_bounds<1>{2, 1} }; + span s = i; + CHECK(s.length() == 0 && s.data() == &i); #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 sav_cv{ src, {2, 1} }; -#else - strided_span sav_cv{ span{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 src{ arr }; - - strided_span 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 sav_cv{ src, {2, 1} }; -#else - strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; + } + + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = i; + CHECK(s.length() == 2 && s.data() == &i); #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 src{ arr }; - - strided_span 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 sav_cv{ src, {2, 1} }; -#else - strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_a_temp = []() -> int { return 4; }; + auto use_a_span = [](span s) { (void) s; }; + use_a_span(get_a_temp()); #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 src{ arr }; - - strided_span 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 av(arr, 2); - span av2{ av }; - CHECK(av2[1] == 5); - - static_assert(std::is_convertible, span>::value, "ctor is not implicit!"); - - const strided_span src{ arr, {2, 1} }; - strided_span sav{ src }; - CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); - CHECK(sav.bounds().stride() == 1); - CHECK(sav[1] == 5); - - static_assert(std::is_convertible, strided_span>::value, "ctor is not implicit!"); - } - - // Check copy constructor - { - int arr1[2] = { 3, 4 }; - const strided_span src1{ arr1, {2, 1} }; - strided_span 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 src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; - strided_span 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 src{ arr1, {{2}, {1}} }; - strided_span sav{ arr2, {{3}, {2}} }; - strided_span& 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 src1{ arr1, {2, 1} }; - strided_span sav1{ arr1b, {1, 1} }; - strided_span& 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 src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; - strided_span sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; - strided_span& 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 data(5 * 10); - std::iota(begin(data), end(data), 0); - const span src = as_span(data).as_span(dim<5>(), dim<10>()); - - const strided_span sav{ src, {{5, 10}, {10, 1}} }; + } + } + + TEST(from_pointer_length_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + int* p = nullptr; + span s{p, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { span s{p, 2}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_pointer_pointer_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{p, p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_array_constructor) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - const strided_span csav{ {src},{ { 5, 10 },{ 10, 1 } } }; + span s{arr}; #endif - const strided_span csav{ span{ src }, { { 5, 10 },{ 10, 1 } } }; - - strided_span sav_sl = sav[2]; - CHECK(sav_sl[0] == 20); - CHECK(sav_sl[9] == 29); - - strided_span 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 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 cm_sl = cm_sav[3]; - - CHECK(cm_sl[0] == 10); - CHECK(cm_sl[1] == 11); - CHECK(cm_sl[2] == 12); - - // Section - strided_span 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 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 sav{ av,{ { 4 },{} } }; - CHECK(sav[0] == 0); - CHECK(sav[3] == 0); - CHECK_THROW(sav[4], fail_fast); - } - - { - // zero extent - strided_span sav{ av,{ {},{ 1 } } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // zero extent and stride - strided_span sav{ av,{ {},{} } }; - CHECK_THROW(sav[0], fail_fast); - } - - { - // strided array ctor with matching strided bounds - strided_span 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 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 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 { arr, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_span { arr, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_span { arr, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_span { arr, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_span { arr, { 5, 5 } }), fail_fast); - } - - { - // bounds cross data boundaries - from array view - CHECK_THROW((strided_span { av, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_span { av, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_span { av, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_span { av, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_span { av, { 5, 5 } }), fail_fast); - } - - { - // bounds cross data boundaries - from dynamic arrays - CHECK_THROW((strided_span { av.data(), 4, { 3, 2 } }), fail_fast); - CHECK_THROW((strided_span { av.data(), 4, { 3, 3 } }), fail_fast); - CHECK_THROW((strided_span { av.data(), 4, { 4, 5 } }), fail_fast); - CHECK_THROW((strided_span { av.data(), 4, { 5, 1 } }), fail_fast); - CHECK_THROW((strided_span { av.data(), 4, { 5, 5 } }), fail_fast); - CHECK_THROW((strided_span { av.data(), 2, { 2, 2 } }), fail_fast); - } + } + + { + span s{arr}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { + span s{arr2d}; + CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); + } + { #ifdef CONFIRM_COMPILATION_ERRORS - { - strided_span sav0{ av.data(), { 3, 2 } }; - strided_span sav1{ arr, { 1 } }; - strided_span sav2{ arr, { 1,1,1 } }; - strided_span sav3{ av, { 1 } }; - strided_span sav4{ av, { 1,1,1 } }; - strided_span sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } }; - strided_span sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } }; - strided_span sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; - - index<1> index{ 0, 1 }; - strided_span sav8{ arr,{ 1,{ 1,1 } } }; - strided_span sav9{ arr,{ { 1,1 },{ 1,1 } } }; - strided_span sav10{ av,{ 1,{ 1,1 } } }; - strided_span sav11{ av,{ { 1,1 },{ 1,1 } } }; - strided_span sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; - strided_span sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; - strided_span sav14{ av.as_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; - } + span s{arr2d}; #endif - } + } - TEST(strided_span_type_conversion) - { - int arr[] = { 0, 1, 2, 3 }; - span av(arr); + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } - { - strided_span sav{ av.data(), av.size(), { av.size() / 2, 2 } }; + { #ifdef CONFIRM_COMPILATION_ERRORS - strided_span lsav1 = sav.as_strided_span(); + span s{arr2d}; #endif - } - { - strided_span sav{ av, { av.size() / 2, 2 } }; + } + + { + span s{arr2d[0]}; + CHECK(s.length() == 1 && s.data() == &arr2d[0]); + } + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; + CHECK(workaround_macro()); + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - strided_span lsav1 = sav.as_strided_span(); + span s{arr2d}; #endif - } - - span bytes = av.as_bytes(); - - // retype strided array with regular strides - from raw data - { - strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - strided_span sav2{ bytes.data(), bytes.size(), bounds }; - strided_span sav3 = sav2.as_strided_span(); - 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); - strided_span sav2{ bytes2, bounds }; - strided_span sav3 = sav2.as_strided_span(); - 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); - strided_span sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_span(), fail_fast); - } - - // retype strided array with not enough elements - strides are too small - { - strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; - span bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); - strided_span sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); - strided_span sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = bytes.as_span(dim<2>(), dim<>(bytes.size() / 2)); - strided_span sav2{ bytes2, bounds }; - CHECK_THROW(sav2.as_strided_span(), fail_fast); - } - - // retype strided array with irregular strides - from raw data - { - strided_bounds<1> bounds{ bytes.size() / 2, 2 }; - strided_span sav2{ bytes.data(), bytes.size(), bounds }; - CHECK_THROW(sav2.as_strided_span(), fail_fast); - } - - // retype strided array with irregular strides - from span - { - strided_bounds<1> bounds{ bytes.size() / 2, 2 }; - strided_span sav2{ bytes, bounds }; - CHECK_THROW(sav2.as_strided_span(), fail_fast); - } - } - - TEST(empty_arrays) - { + } + + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[11] == 12); + } + + { + span s{arr3d}; + CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - { - span empty; - strided_span empty2; - strided_span empty3{ nullptr,{ 0, 1 } }; - } + span s{arr3d}; #endif + } - { - span empty_av(nullptr); - - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) - { - (void)v; - CHECK(false); - } - } - - { - span empty_av = {}; - CHECK(empty_av.bounds().index_bounds() == index<1>{ 0 }); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) - { - (void)v; - CHECK(false); - } - } - - { - span empty_av(nullptr); - strided_span 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 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); - } - } - } - - TEST(index_constructor) - { - auto arr = new int[8]; - for (int i = 0; i < 4; ++i) - { - arr[2 * i] = 4 + i; - arr[2 * i + 1] = i; - } - - span av(arr, 8); - - ptrdiff_t a[1] = { 0 }; - index<1> i = a; - - CHECK(av[i] == 4); - - auto av2 = av.as_span(dim<4>(), dim<>(2)); - ptrdiff_t a2[2] = { 0, 1 }; - index<2> i2 = a2; - - CHECK(av2[i2] == 0); - CHECK(av2[0][i] == 4); - - delete[] arr; - } - - TEST(index_constructors) - { - { - // components of the same type - index<3> i1(0, 1, 2); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - size_t c1 = 1; - index<3> i2(c0, c1, 2); - CHECK(i2[0] == 0); - - // from array - index<3> i3 = { 0,1,2 }; - CHECK(i3[0] == 0); - - // from other index of the same size type - index<3> i4 = i3; - CHECK(i4[0] == 0); - - // default - index<3> i7; - CHECK(i7[0] == 0); - - // default - index<3> i9 = {}; - CHECK(i9[0] == 0); - } - - { - // components of the same type - index<1> i1(0); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - index<1> i2(c0); - CHECK(i2[0] == 0); - - // from array - index<1> i3 = { 0 }; - CHECK(i3[0] == 0); - - // from int - index<1> i4 = 0; - CHECK(i4[0] == 0); - - // from other index of the same size type - index<1> i5 = i3; - CHECK(i5[0] == 0); - - // default - index<1> i8; - CHECK(i8[0] == 0); - - // default - index<1> i9 = {}; - CHECK(i9[0] == 0); - } + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + { #ifdef CONFIRM_COMPILATION_ERRORS - { - index<3> i1(0, 1); - index<3> i2(0, 1, 2, 3); - index<3> i3 = { 0 }; - index<3> i4 = { 0, 1, 2, 3 }; - index<1> i5 = { 0,1 }; - } + span s{arr3d}; #endif - } - - TEST(index_operations) - { - ptrdiff_t a[3] = { 0, 1, 2 }; - ptrdiff_t b[3] = { 3, 4, 5 }; - index<3> i = a; - index<3> j = b; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - - { - index<3> k = i + j; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 3); - CHECK(k[1] == 5); - CHECK(k[2] == 7); - } - - { - index<3> k = i * 3; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<3> k = 3 * i; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<2> k = details::shift_left(i); - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 1); - CHECK(k[1] == 2); - } - - } - - void iterate_second_column(span av) - { - auto length = av.size() / 2; - - // view to the second column - auto section = av.section({ 0,1 }, { length,1 }); - - CHECK(section.size() == length); - for (auto i = 0; i < section.size(); ++i) - { - CHECK(section[i][0] == av[i][1]); - } - - for (auto i = 0; i < section.size(); ++i) - { - auto idx = index<2>{ i,0 }; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - - CHECK(section.bounds().index_bounds()[0] == length); - CHECK(section.bounds().index_bounds()[1] == 1); - for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) - { - for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) - { - auto idx = index<2>{ i,j }; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - } - - size_t check_sum = 0; - for (auto i = 0; i < length; ++i) - { - check_sum += av[i][1]; - } - - { - auto idx = 0; - size_t sum = 0; - for (auto num : section) - { - CHECK(num == av[idx][1]); - sum += num; - idx++; - } - - CHECK(sum == check_sum); - } - { - size_t idx = length - 1; - size_t sum = 0; - for (auto iter = section.rbegin(); iter != section.rend(); ++iter) - { - CHECK(*iter == av[idx][1]); - sum += *iter; - idx--; - } - - CHECK(sum == check_sum); - } - } - - TEST(span_section_iteration) - { - int arr[4][2] = { { 4,0 },{ 5,1 },{ 6,2 },{ 7,3 } }; - - // static bounds - { - span av = arr; - iterate_second_column(av); - } - // first bound is dynamic - { - span av = arr; - iterate_second_column(av); - } - // second bound is dynamic - { - span av = arr; - iterate_second_column(av); - } - // both bounds are dynamic - { - span av = arr; - iterate_second_column(av); - } - } - - TEST(dynamic_span_section_iteration) - { - auto height = 4, 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(arr, size); - - // first bound is dynamic - { - span av2 = av.as_span(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // second bound is dynamic - { - span av2 = av.as_span(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // both bounds are dynamic - { - span av2 = av.as_span(dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - - delete[] arr; - } - - void iterate_every_other_element(span 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 }); + } + + { + span s{arr3d[0]}; + CHECK(s.length() == 1 && s.data() == &arr3d[0]); + } + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; #endif - strided_span 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 av(arr, 8); - iterate_every_other_element(av); - } - - // dynamic bounds - { - span 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 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 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(arr, 24).as_span(dim<3>(),dim<4>(),dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_span(arr, 24).as_span(dim<>(3), dim<4>(), dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_span(arr, 24).as_span(dim<3>(), dim<>(4), dim<2>()); - iterate_second_slice(av); - } - - { - auto av = as_span(arr, 24).as_span(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(arr, 4).as_bytes().as_span(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(); // { { 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 transposed{ cs.data(), cs.bounds().total_size(), reverse_bounds }; - - // slice to get a one-dimensional array of c's - strided_span 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++; - } + } + } + + TEST(from_dynamic_array_constructor) + { + double(*arr)[3][4] = new double[100][3][4]; + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + CHECK_THROW(s[10][3][4], fail_fast); + } + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } - } + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } - TEST(constructors) - { - span av(nullptr); - CHECK(av.length() == 0); + { + span s(arr, 0); + CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); + } - span av2; - CHECK(av2.length() == 0); + delete[] arr; + } - span av3(nullptr, 0); - CHECK(av3.length() == 0); + TEST(from_std_array_constructor) + { + std::array arr = {1, 2, 3, 4}; - // Constructing from a nullptr + length is specifically disallowed - auto f = [&]() {span av4(nullptr, 2);}; - CHECK_THROW(f(), fail_fast); + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - int arr1[2][3]; - span av5(arr1); + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } - array arr2; - span av6(arr2); + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - vector vec1(19); - span av7(vec1); - CHECK(av7.length() == 19); + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); - span av8; - CHECK(av8.length() == 0); - span av9(arr2); - CHECK(av9.length() == 15); + span cs{arr}; + CHECK(cs.size() == 2 && cs.data() == arr.data()); + } + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + span cs{arr}; + CHECK(cs.size() == 0 && cs.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { #ifdef CONFIRM_COMPILATION_ERRORS - span av10; - DerivedClass *p = nullptr; - span av11(p, 0); -#endif - } - - TEST(copyandassignment) - { - span av1; - - int arr[] = {3, 4, 5}; - av1 = arr; - span av2; - av2 = av1; - } - - TEST(span_first) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - span av = arr; - CHECK((av.first<2>().bounds() == static_bounds<2>())); - CHECK(av.first<2>().length() == 2); - CHECK(av.first(2).length() == 2); - } - - { - span av = arr; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - - { - span av = arr; - CHECK((av.first<5>().bounds() == static_bounds<5>())); - CHECK(av.first<5>().length() == 5); - CHECK(av.first(5).length() == 5); - } - - { - span av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK(av.first<6>().bounds() == static_bounds<6>()); - CHECK(av.first<6>().length() == 6); + span s{arr}; #endif - CHECK_THROW(av.first(6).length(), fail_fast); - } - - { - span av; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - } - - TEST(span_last) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - span av = arr; - CHECK((av.last<2>().bounds() == static_bounds<2>())); - CHECK(av.last<2>().length() == 2); - CHECK(av.last(2).length() == 2); - } - - { - span av = arr; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - - { - span av = arr; - CHECK((av.last<5>().bounds() == static_bounds<5>())); - CHECK(av.last<5>().length() == 5); - CHECK(av.last(5).length() == 5); - } - - - { - span av = arr; + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - CHECK((av.last<6>().bounds() == static_bounds<6>())); - CHECK(av.last<6>().length() == 6); + auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); #endif - CHECK_THROW(av.last(6).length(), fail_fast); - } - - { - span av; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - } - - TEST(customized_span_size) - { - double (*arr)[3][4] = new double[100][3][4]; - span av1(arr, 10); - - struct EffectiveStructure - { - double* v1; - ptrdiff_t v2; - }; - CHECK(sizeof(av1) == sizeof(EffectiveStructure)); - - CHECK_THROW(av1[10][3][4], fail_fast); - - span av2 = av1.as_span(dim<>(5), dim<6>(), dim<4>()); - (void)av2; - } - - TEST(span_sub) - { - int arr[5] = { 1, 2, 3, 4, 5 }; - - { - span av = arr; - CHECK((av.sub<2,2>().bounds() == static_bounds<2>())); - CHECK((av.sub<2,2>().length() == 2)); - CHECK(av.sub(2,2).length() == 2); - CHECK(av.sub(2,3).length() == 3); - } - - - { - span av = arr; - CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<0,0>().length() == 0)); - CHECK(av.sub(0,0).length() == 0); - } - - { - span av = arr; - CHECK((av.sub<0,5>().bounds() == static_bounds<5>())); - CHECK((av.sub<0,5>().length() == 5)); - CHECK(av.sub(0,5).length() == 5); - CHECK_THROW(av.sub(0,6).length(), fail_fast); - CHECK_THROW(av.sub(1,5).length(), fail_fast); - } - - { - span av = arr; - CHECK((av.sub<5,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<5, 0>().length() == 0)); - CHECK(av.sub(5,0).length() == 0); - CHECK_THROW(av.sub(6,0).length(), fail_fast); - } - - { - span av; - CHECK((av.sub<0,0>().bounds() == static_bounds<0>())); - CHECK((av.sub<0,0>().length() == 0)); - CHECK(av.sub(0,0).length() == 0); - CHECK_THROW((av.sub<1,0>().length()), fail_fast); - } + } + } + + TEST(from_const_std_array_constructor) + { + const std::array arr = {1, 2, 3, 4}; { - span av; - CHECK(av.sub(0).length() == 0); - CHECK_THROW(av.sub(1).length(), fail_fast); + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } { - span av = arr; - CHECK(av.sub(0).length() == 5); - CHECK(av.sub(1).length() == 4); - CHECK(av.sub(4).length() == 1); - CHECK(av.sub(5).length() == 0); - CHECK_THROW(av.sub(6).length(), fail_fast); - auto av2 = av.sub(1); - for (int i = 0; i < 4; ++i) - CHECK(av2[i] == i+2); - } - - { - span av = arr; - CHECK(av.sub(0).length() == 5); - CHECK(av.sub(1).length() == 4); - CHECK(av.sub(4).length() == 1); - CHECK(av.sub(5).length() == 0); - CHECK_THROW(av.sub(6).length(), fail_fast); - auto av2 = av.sub(1); - for (int i = 0; i < 4; ++i) - CHECK(av2[i] == i+2); + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } - } - void AssertNullEmptyProperties(span& av) - { - CHECK(av.length() == 0); - CHECK(av.data() == nullptr); - CHECK(!av); - } + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + } - template - void AssertContentsMatch(T a1, U a2) - { - CHECK(a1.length() == a2.length()); - for (auto i = 0; i < a1.length(); ++i) - CHECK(a1[i] == a2[i]); - } + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + } - TEST(TestNullConstruction) - { - span av; - AssertNullEmptyProperties(av); + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} - span av2(nullptr); - AssertNullEmptyProperties(av2); - } + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } - TEST(ArrayConstruction) - { - int a[] = { 1, 2, 3, 4 }; + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } - span av = { &a[1], 3 }; - CHECK(av.length() == 3); + TEST(from_container_constructor) + { + std::vector v = {1, 2, 3}; + const std::vector cv = v; - span av3 = { a, 2 }; - CHECK(av3.length() == 2); + { + span s{v}; + CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); - span av2 = a; - CHECK(av2.length() == 4); - } + span cs{v}; + CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); + } - TEST(NonConstConstConversions) - { - int a[] = { 1, 2, 3, 4 }; + std::string str = "hello"; + const std::string cstr = "hello"; + { #ifdef CONFIRM_COMPILATION_ERRORS - span cav = a; - span av = cav; -#else - span av = a; - span cav = av; + span s{str}; + CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); #endif - AssertContentsMatch(av, cav); - } - - TEST(FixedSizeConversions) - { - int arr[] = { 1, 2, 3, 4 }; - - // converting to an span from an equal size array is ok - span av4 = arr; - CHECK(av4.length() == 4); - - // converting to dynamic_range a_v is always ok - { - span av = av4; - (void)av; - } - { - span av = arr; - (void)av; - } - - // initialization or assignment to static span that REDUCES size is NOT ok + span cs{str}; + CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - { - span av2 = arr; - } - { - span av2 = av4; - } + span s{cstr}; #endif + span cs{cstr}; + CHECK(cs.size() == narrow_cast(cstr.size()) && + cs.data() == cstr.data()); + } - { - span av = arr; - span av2 = av; - (void)av2; - } - + { #ifdef CONFIRM_COMPILATION_ERRORS - { - span av = arr; - span av2 = av.as_span(dim<2>(), dim<2>()); - } + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); #endif + } - { - span av = arr; - auto f = [&]() {span av2 = av.as_span(dim<>(2), dim<>(2)); (void)av2; }; - CHECK_THROW(f(), fail_fast); - } - - // but doing so explicitly is ok - - // you can convert statically - { - span av2 = {arr, 2}; - (void)av2; - } - { - span av2 = av4.first<1>(); - (void)av2; - } - - // ...or dynamically - { - // NB: implicit conversion to span from span - span av2 = av4.first(1); - (void)av2; - } - - // initialization or assignment to static span that requires size INCREASE is not ok. - int arr2[2] = { 1, 2 }; + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + { #ifdef CONFIRM_COMPILATION_ERRORS - { - span av4 = arr2; - } - { - span av2 = arr2; - span av4 = av2; - } + auto get_temp_vector = []() -> const std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); #endif - { - auto f = [&]() {span av4 = {arr2, 2}; (void)av4; }; - CHECK_THROW(f(), fail_fast); - } - - // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one - span av = arr2; - auto f = [&](){ span av2 = av; (void)av2; }; - CHECK_THROW(f(), fail_fast); - } - - TEST(AsWriteableBytes) - { - int a[] = { 1, 2, 3, 4 }; - - { + } + + { #ifdef CONFIRM_COMPILATION_ERRORS - // you should not be able to get writeable bytes for const objects - span av = a; - auto wav = av.as_writeable_bytes(); + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); #endif - } - - { - span av; - auto wav = av.as_writeable_bytes(); - CHECK(wav.length() == av.length()); - CHECK(wav.length() == 0); - CHECK(wav.bytes() == 0); - } - - { - span av = a; - auto wav = av.as_writeable_bytes(); - CHECK(wav.data() == (byte*)&a[0]); - CHECK(wav.length() == sizeof(a)); - } - } - - TEST(NonConstIterator) - { - int a[] = { 1, 2, 3, 4 }; - - { - span av = a; - auto wav = av.as_writeable_bytes(); - for (auto& b : wav) - { - b = byte(0); - } - for (size_t i = 0; i < 4; ++i) - { - CHECK(a[i] == 0); - } - } - - { - span av = a; - for (auto& n : av) - { - n = 1; - } - for (size_t i = 0; i < 4; ++i) - { - CHECK(a[i] == 1); - } - } - } - - TEST(ArrayViewComparison) - { - { - int arr[10][2]; - auto av1 = as_span(arr); - span av2 = av1; - - CHECK(av1 == av2); - - span av3 = av1.as_span(dim<>(20)); - CHECK(av3 == av2 && av3 == av1); - } - - { - auto av1 = nullptr; - auto av2 = nullptr; - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 2, 1 }; // bigger - - span av1 = nullptr; - span av2 = arr; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; - int arr2[] = { 1, 2 }; - span av1 = arr1; - span av2 = arr2; - - CHECK(av1 == av2); - CHECK(!(av1 != av2)); - CHECK(!(av1 < av2)); - CHECK(av1 <= av2); - CHECK(!(av1 > av2)); - CHECK(av1 >= av2); - CHECK(av2 == av1); - CHECK(!(av2 != av1)); - CHECK(!(av2 < av1)); - CHECK(av2 <= av1); - CHECK(!(av2 > av1)); - CHECK(av2 >= av1); - } - - { - int arr[] = { 1, 2, 3 }; - - span av1 = { &arr[0], 2 }; // shorter - span av2 = arr; // longer - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - - { - int arr1[] = { 1, 2 }; // smaller - int arr2[] = { 2, 1 }; // bigger - - span av1 = arr1; - span av2 = arr2; - - CHECK(av1 != av2); - CHECK(av2 != av1); - CHECK(!(av1 == av2)); - CHECK(!(av2 == av1)); - CHECK(av1 < av2); - CHECK(!(av2 < av1)); - CHECK(av1 <= av2); - CHECK(!(av2 <= av1)); - CHECK(av2 > av1); - CHECK(!(av1 > av2)); - CHECK(av2 >= av1); - CHECK(!(av1 >= av2)); - } - } -} + } -int main(int, const char *[]) -{ - return UnitTest::RunAllTests(); -} + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map m; + span s{m}; +#endif + } + } + + TEST(from_convertible_span_constructor) + { +#ifdef CONFIRM_COMPILATION_ERRORS + span av1(nullptr, b1); + + auto f = [&]() { span av1(nullptr); }; + CHECK_THROW(f(), fail_fast); +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + span av1 = nullptr; + span av2(av1); + span av2(av1); +#endif + + span avd; +#ifdef CONFIRM_COMPILATION_ERRORS + span avb = avd; +#endif + span avcd = avd; + (void) avcd; + } + + TEST(copy_move_and_assignment) + { + span s1; + CHECK(s1.empty()); + + int arr[] = {3, 4, 5}; + + span s2 = arr; + CHECK(s2.length() == 3 && s2.data() == &arr[0]); + + s2 = s1; + CHECK(s2.empty()); + + auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; + auto use_span = [&](span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; + use_span(get_temp_span()); + + s1 = get_temp_span(); + CHECK(s1.length() == 2 && s1.data() == &arr[1]); + } + + template + void fn(const Bounds&) + { + static_assert(Bounds::static_size == 60, "static bounds is wrong size"); + } + TEST(as_span_reshape) + { + int a[3][4][5]; + auto av = as_span(a); + fn(av.bounds()); + auto av2 = as_span(av, dim<60>()); + auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); + auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); + auto av6 = as_span(av5, dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = as_bytes(av6); + + auto av8 = as_span(av7); + + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) { + CHECK(av8[i] == 1); + } + } + + TEST(first) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.first<2>().bounds() == static_bounds<2>())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + span av = arr; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + span av = arr; + CHECK((av.first<5>().bounds() == static_bounds<5>())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds<6>()); + CHECK(av.first<6>().length() == 6); + CHECK(av.first<-1>().length() == -1); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + span av; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(last) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.last<2>().bounds() == static_bounds<2>())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + span av = arr; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + span av = arr; + CHECK((av.last<5>().bounds() == static_bounds<5>())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds<6>())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + span av; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(subspan) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); + CHECK((av.subspan<2, 2>().length() == 2)); + CHECK(av.subspan(2, 2).length() == 2); + CHECK(av.subspan(2, 3).length() == 3); + } + + { + span av = arr; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + } + + { + span av = arr; + CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); + CHECK((av.subspan<0, 5>().length() == 5)); + CHECK(av.subspan(0, 5).length() == 5); + CHECK_THROW(av.subspan(0, 6).length(), fail_fast); + CHECK_THROW(av.subspan(1, 5).length(), fail_fast); + } + + { + span av = arr; + CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<5, 0>().length() == 0)); + CHECK(av.subspan(5, 0).length() == 0); + CHECK_THROW(av.subspan(6, 0).length(), fail_fast); + } + + { + span av; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); + } + + { + span av; + CHECK(av.subspan(0).length() == 0); + CHECK_THROW(av.subspan(1).length(), fail_fast); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + } + + TEST(rank) + { + int arr[2] = {1, 2}; + + { + span s; + CHECK(s.rank() == 1); + } + + { + span s = arr; + CHECK(s.rank() == 1); + } + + int arr2d[1][1] = {}; + { + span s = arr2d; + CHECK(s.rank() == 2); + } + } + + TEST(extent) + { + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(s.extent<1>() == 0); +#endif + } + + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 1); + CHECK(s.extent<0>() == 1); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 1); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 0); + CHECK(s.extent<0>() == 0); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 0); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + } + + TEST(operator_function_call) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s = arr; + CHECK(s(0) == 1); + CHECK_THROW(s(5), fail_fast); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s = arr2d; + CHECK(s(0, 0) == 1); + CHECK(s(1, 2) == 6); + } + } + + TEST(comparison_operators) + { + { + int arr[10][2]; + auto s1 = as_span(arr); + span s2 = s1; + + CHECK(s1 == s2); + + span s3 = as_span(s1, dim<>(20)); + CHECK(s3 == s2 && s3 == s1); + } + + { + auto s1 = nullptr; + auto s2 = nullptr; + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {2, 1}; // bigger + + span s1 = nullptr; + span s2 = arr; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {1, 2, 3}; + + span s1 = {&arr[0], 2}; // shorter + span s2 = arr; // longer + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + } + + TEST(basics) + { + auto ptr = as_span(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) { + CHECK(num == 99); + } + + delete[] ptr.data(); + } + + TEST(bounds_checks) + { + int arr[10][2]; + auto av = as_span(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10, 2}]), fail_fast); + } + + void overloaded_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void overloaded_func(span exp, char expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void fixed_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + TEST(span_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_span(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + + // fixed_func(av, 34); + delete[] data; + } + + TEST(md_access) + { + auto width = 5, height = 20; + + auto imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = + as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (auto i = 0; i < height; i++) { + for (auto j = 0; j < width; j++) { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(as_span) + { + { + int* arr = new int[150]; + + auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + array stdarr{0}; + auto av2 = as_span(stdarr); + overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + (void) t; + auto av3 = as_span(str); + overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + string str; + span strspan = as_span(str); + (void) strspan; + const string cstr; + span cstrspan = as_span(cstr); + (void) cstrspan; + } + + { + int a[3][4][5]; + auto av = as_span(a); + const int(*b)[4][5]; + b = a; + auto bv = as_span(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_span(arr); + (void) cv; + + vector vec(3); + auto dv = as_span(vec); + (void) dv; + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_span(std::move(vec)); +#endif + } + } + + TEST(empty_spans) + { + { + span empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + + { + span empty_av = {}; + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + span av(arr, 8); + + ptrdiff_t a[1] = {0}; + index<1> i = a; + + CHECK(av[i] == 4); + + auto av2 = as_span(av, dim<4>(), dim<>(2)); + ptrdiff_t a2[2] = {0, 1}; + index<2> i2 = a2; + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = {0, 1, 2}; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = {0}; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = {0}; + index<3> i4 = {0, 1, 2, 3}; + index<1> i5 = {0, 1}; + } +#endif + } + + TEST(index_operations) + { + ptrdiff_t a[3] = {0, 1, 2}; + ptrdiff_t b[3] = {3, 4, 5}; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + } + + void iterate_second_column(span av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({0, 1}, {length, 1}); + + CHECK(section.size() == length); + for (auto i = 0; i < section.size(); ++i) { + CHECK(section[i][0] == av[i][1]); + } + + for (auto i = 0; i < section.size(); ++i) { + auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { + auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + } + + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) { + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); + } + } + + TEST(span_section_iteration) + { + int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; + + // static bounds + { + span av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + span av = arr; + iterate_second_column(av); + } + } + + TEST(dynamic_span_section_iteration) + { + auto height = 4, 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(arr, size); + + // first bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + TEST(span_structure_size) + { + double(*arr)[3][4] = new double[100][3][4]; + span av1(arr, 10); + + struct EffectiveStructure + { + double* v1; + ptrdiff_t v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + (void) av2; + } + + TEST(fixed_size_conversions) + { + int arr[] = {1, 2, 3, 4}; + + // converting to an span from an equal size array is ok + span av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + span av = av4; + (void) av; + } + { + span av = arr; + (void) av; + } + +// initialization or assignment to static span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av2 = arr; + } + { + span av2 = av4; + } +#endif + + { + span av = arr; + span av2 = av; + (void) av2; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av = arr; + span av2 = av.as_span(dim<2>(), dim<2>()); + } +#endif + + { + span av = arr; + span av2 = as_span(av, dim<>(2), dim<>(2)); + auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; + CHECK(workaround_macro()); + } + + // but doing so explicitly is ok + + // you can convert statically + { + span av2 = {arr, 2}; + (void) av2; + } + { + span av2 = av4.first<1>(); + (void) av2; + } + + // ...or dynamically + { + // NB: implicit conversion to span from span + span av2 = av4.first(1); + (void) av2; + } + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av4 = arr2; + } + { + span av2 = arr2; + span av4 = av2; + } +#endif + { + auto f = [&]() { + span av4 = {arr2, 2}; + (void) av4; + }; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + span av = arr2; + auto f = [&]() { + span av2 = av; + (void) av2; + }; + CHECK_THROW(f(), fail_fast); + } + + TEST(as_writeable_bytes) + { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + span av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + span av; + auto wav = as_writeable_bytes(av); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.size_bytes() == 0); + } + + { + span av = a; + auto wav = as_writeable_bytes(av); + CHECK(wav.data() == (byte*) &a[0]); + CHECK(wav.length() == sizeof(a)); + } + } + + TEST(iterator) + { + int a[] = {1, 2, 3, 4}; + + { + span av = a; + auto wav = as_writeable_bytes(av); + for (auto& b : wav) { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 0); + } + } + + { + span av = a; + for (auto& n : av) { + n = 1; + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 1); + } + } + } +} + +int main(int, const char* []) { return UnitTest::RunAllTests(); } diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp new file mode 100644 index 0000000..0fbf1d7 --- /dev/null +++ b/tests/strided_span_tests.cpp @@ -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 +#include + +#include +#include +#include +#include +#include +#include + +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 data(5 * 10); + std::iota(begin(data), end(data), 0); + const span av = as_span(span{data}, dim<5>(), dim<10>()); + + strided_span 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 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 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 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 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 src = arr; + + strided_span 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 sav_c{ {src}, {2, 1} }; + strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; +#else + strided_span sav_c{ span{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 sav_v{ src, {2, 1} }; +#else + strided_span sav_v{ span{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 sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 sav_cv{ src, {2, 1} }; +#else + strided_span sav_cv{ span{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 src{ arr }; + + strided_span 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 av(arr, 2); + span av2{ av }; + CHECK(av2[1] == 5); + + static_assert(std::is_convertible, span>::value, "ctor is not implicit!"); + + const strided_span src{ arr, {2, 1} }; + strided_span sav{ src }; + CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); + CHECK(sav.bounds().stride() == 1); + CHECK(sav[1] == 5); + + static_assert(std::is_convertible, strided_span>::value, "ctor is not implicit!"); + } + + // Check copy constructor + { + int arr1[2] = { 3, 4 }; + const strided_span src1{ arr1, {2, 1} }; + strided_span 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 src2{ arr2, {{ 3, 2 }, { 2, 1 }} }; + strided_span 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 src{ arr1, {{2}, {1}} }; + strided_span sav{ arr2, {{3}, {2}} }; + strided_span& 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 src1{ arr1, {2, 1} }; + strided_span sav1{ arr1b, {1, 1} }; + strided_span& 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 src2{ arr2, {{ 3, 2 },{ 2, 1 }} }; + strided_span sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} }; + strided_span& 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 data(5 * 10); + std::iota(begin(data), end(data), 0); + const span src = as_span(span{data}, dim<5>(), dim<10>()); + + const strided_span sav{ src, {{5, 10}, {10, 1}} }; +#ifdef CONFIRM_COMPILATION_ERRORS + const strided_span csav{ {src},{ { 5, 10 },{ 10, 1 } } }; +#endif + const strided_span csav{ span{ src }, { { 5, 10 },{ 10, 1 } } }; + + strided_span sav_sl = sav[2]; + CHECK(sav_sl[0] == 20); + CHECK(sav_sl[9] == 29); + + strided_span 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 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 cm_sl = cm_sav[3]; + + CHECK(cm_sl[0] == 10); + CHECK(cm_sl[1] == 11); + CHECK(cm_sl[2] == 12); + + // Section + strided_span 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 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 sav{ av,{ { 4 },{} } }; + CHECK(sav[0] == 0); + CHECK(sav[3] == 0); + CHECK_THROW(sav[4], fail_fast); + } + + { + // zero extent + strided_span sav{ av,{ {},{ 1 } } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // zero extent and stride + strided_span sav{ av,{ {},{} } }; + CHECK_THROW(sav[0], fail_fast); + } + + { + // strided array ctor with matching strided bounds + strided_span 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 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 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 { arr, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { arr, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from array view + CHECK_THROW((strided_span { av, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av, { 5, 5 } }), fail_fast); + } + + { + // bounds cross data boundaries - from dynamic arrays + CHECK_THROW((strided_span { av.data(), 4, { 3, 2 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 3, 3 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 4, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 1 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 4, { 5, 5 } }), fail_fast); + CHECK_THROW((strided_span { av.data(), 2, { 2, 2 } }), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + strided_span sav0{ av.data(), { 3, 2 } }; + strided_span sav1{ arr, { 1 } }; + strided_span sav2{ arr, { 1,1,1 } }; + strided_span sav3{ av, { 1 } }; + strided_span sav4{ av, { 1,1,1 } }; + strided_span sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } }; + strided_span sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } }; + strided_span sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + + index<1> index{ 0, 1 }; + strided_span sav8{ arr,{ 1,{ 1,1 } } }; + strided_span sav9{ arr,{ { 1,1 },{ 1,1 } } }; + strided_span sav10{ av,{ 1,{ 1,1 } } }; + strided_span sav11{ av,{ { 1,1 },{ 1,1 } } }; + strided_span sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_span sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_span 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 av(arr); + + { + strided_span sav{ av.data(), av.size(), { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + { + strided_span sav{ av, { av.size() / 2, 2 } }; +#ifdef CONFIRM_COMPILATION_ERRORS + strided_span lsav1 = sav.as_strided_span(); +#endif + } + + span 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 sav2{ bytes.data(), bytes.size(), bounds }; + strided_span sav3 = sav2.as_strided_span(); + 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 bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + strided_span sav3 = sav2.as_strided_span(); + 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 bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with not enough elements - strides are too small + { + strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; + span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), 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 bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + strided_span sav2{ bytes2, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from raw data + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes.data(), bytes.size(), bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + + // retype strided array with irregular strides - from span + { + strided_bounds<1> bounds{ bytes.size() / 2, 2 }; + strided_span sav2{ bytes, bounds }; + CHECK_THROW(sav2.as_strided_span(), fail_fast); + } + } + + TEST(empty_strided_spans) + { + { + span empty_av(nullptr); + strided_span 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 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 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 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 av(arr, 8); + iterate_every_other_element(av); + } + + // dynamic bounds + { + span 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 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 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(); // { { 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 transposed{cs.data(), cs.bounds().total_size(), reverse_bounds}; + + // slice to get a one-dimensional array of c's + strided_span 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(); +} diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index efdf0ff..f14df93 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -58,25 +58,21 @@ SUITE(string_span_tests) { cwstring_span<> v = ensure_z(stack_string); CHECK(v.length() == 5); - CHECK(v.used_length() == v.length()); } { cwstring_span<> v = stack_string; CHECK(v.length() == 6); - CHECK(v.used_length() == v.length()); } { wstring_span<> v = ensure_z(stack_string); CHECK(v.length() == 5); - CHECK(v.used_length() == v.length()); } { wstring_span<> v = stack_string; CHECK(v.length() == 6); - CHECK(v.used_length() == v.length()); } } @@ -85,7 +81,6 @@ SUITE(string_span_tests) const char* s = "Hello"; cstring_span<> v = ensure_z(s); CHECK(v.length() == 5); - CHECK(v.used_length() == v.length()); } TEST(TestConversionToConst) -- cgit v1.2.3 From c9959b107118b7adc8d615a121f94c72214cd0cd Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 30 Nov 2015 05:34:38 +0000 Subject: Corrected some variable naming. --- include/span.h | 54 ++++++++++++++++++++++++++-------------------------- tests/span_tests.cpp | 4 ++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/include/span.h b/include/span.h index 4f0de0b..fd15472 100644 --- a/include/span.h +++ b/include/span.h @@ -840,27 +840,27 @@ public: using index_size_type = typename IndexType::value_type; template explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary(bnd.index_bounds()), - curr(std::move(curr)) + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - constexpr reference operator*() const noexcept { return curr; } + constexpr reference operator*() const noexcept { return curr_; } - constexpr pointer operator->() const noexcept { return &curr; } + constexpr pointer operator->() const noexcept { return &curr_; } constexpr bounds_iterator& operator++() noexcept { for (size_t i = rank; i-- > 0;) { - if (curr[i] < boundary[i] - 1) { - curr[i]++; + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; return *this; } - curr[i] = 0; + curr_[i] = 0; } // If we're here we've wrapped over - set to past-the-end. - curr = boundary; + curr_ = boundary_; return *this; } @@ -873,19 +873,19 @@ public: constexpr bounds_iterator& operator--() noexcept { - if (!less(curr, boundary)) { + if (!less(curr_, boundary_)) { // if at the past-the-end, set to last element for (size_t i = 0; i < rank; ++i) { - curr[i] = boundary[i] - 1; + curr_[i] = boundary_[i] - 1; } return *this; } for (size_t i = rank; i-- > 0;) { - if (curr[i] >= 1) { - curr[i]--; + if (curr_[i] >= 1) { + curr_[i]--; return *this; } - curr[i] = boundary[i] - 1; + curr_[i] = boundary_[i] - 1; } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" @@ -908,18 +908,18 @@ public: constexpr bounds_iterator& operator+=(difference_type n) noexcept { - auto linear_idx = linearize(curr) + n; + auto linear_idx = linearize(curr_) + n; std::remove_const_t stride = 0; stride[rank - 1] = 1; for (size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary[i + 1]; + stride[i] = stride[i + 1] * boundary_[i + 1]; } for (size_t i = 0; i < rank; ++i) { - curr[i] = linear_idx / stride[i]; + curr_[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } // index is out of bounds of the array - Expects(!less(curr, index_type{}) && !less(boundary, curr)); + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); return *this; } @@ -933,21 +933,21 @@ public: constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { - return linearize(curr) - linearize(rhs.curr); + return linearize(curr_) - linearize(rhs.curr_); } constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } constexpr bool operator==(const bounds_iterator& rhs) const noexcept { - return curr == rhs.curr; + return curr_ == rhs.curr_; } constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } constexpr bool operator<(const bounds_iterator& rhs) const noexcept { - return less(curr, rhs.curr); + return less(curr_, rhs.curr_); } constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } @@ -958,8 +958,8 @@ public: void swap(bounds_iterator& rhs) noexcept { - std::swap(boundary, rhs.boundary); - std::swap(curr, rhs.curr); + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); } private: @@ -977,25 +977,25 @@ private: // Check if past-the-end index_size_type multiplier = 1; index_size_type res = 0; - if (!less(idx, boundary)) { + if (!less(idx, boundary_)) { res = 1; for (size_t i = rank; i-- > 0;) { res += (idx[i] - 1) * multiplier; - multiplier *= boundary[i]; + multiplier *= boundary_[i]; } } else { for (size_t i = rank; i-- > 0;) { res += idx[i] * multiplier; - multiplier *= boundary[i]; + multiplier *= boundary_[i]; } } return res; } - value_type boundary; - std::remove_const_t curr; + value_type boundary_; + std::remove_const_t curr_; }; template diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8737db9..8b39639 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -1606,8 +1606,8 @@ SUITE(span_tests) #endif { auto f = [&]() { - span av4 = {arr2, 2}; - (void) av4; + span av9 = {arr2, 2}; + (void) av9; }; CHECK_THROW(f(), fail_fast); } -- cgit v1.2.3 From f76f73980539b978ecf54798f6e6910462405e81 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 30 Nov 2015 18:20:14 -0800 Subject: Addressing #178: wrong return type for function. --- include/span.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index fd15472..6ee810d 100644 --- a/include/span.h +++ b/include/span.h @@ -327,9 +327,9 @@ namespace details } template - bool contains(const T&) const + size_type contains(const T&) const { - return false; + return -1; } size_type elementNum(size_t) const noexcept { return 0; } -- cgit v1.2.3 From e3878a655658d8198fa9ad5ea9df7ad9705bd6db Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 30 Nov 2015 12:24:00 -0800 Subject: merging with master --- include/span.h | 4 +- include/string_span.h | 433 ++++++++++++++++++++++++++++++++++++++++---- tests/string_span_tests.cpp | 401 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 798 insertions(+), 40 deletions(-) diff --git a/include/span.h b/include/span.h index 6ee810d..8151737 100644 --- a/include/span.h +++ b/include/span.h @@ -51,8 +51,8 @@ #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG #define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT -// noexcept is not understood -#ifndef GSL_THROWS_ON_CONTRACT_VIOLATION +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ #endif diff --git a/include/string_span.h b/include/string_span.h index 250c528..b11dd9e 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -23,15 +23,37 @@ #include "span.h" #include -// VS 2013 workarounds #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 { // @@ -56,27 +78,6 @@ using zstring = char*; template using wzstring = wchar_t*; -// -// string_span and relatives -// -// Note that Extent is always single-dimension only -// -template -using basic_string_span = span; - -template -using string_span = basic_string_span; - -template -using cstring_span = basic_string_span; - -template -using wstring_span = basic_string_span; - -template -using cwstring_span = basic_string_span; - - // // ensure_sentinel() // @@ -86,7 +87,7 @@ using cwstring_span = basic_string_span; // Will fail-fast if sentinel cannot be found before max elements are examined. // template -span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; @@ -101,46 +102,341 @@ span ensure_sentinel(const T* seq, std::ptrdiff_t max = PTRDIF // the limit of size_type. // template -inline basic_string_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline basic_string_span ensure_z(char* const& sz, std::ptrdiff_t max) +inline span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline basic_string_span ensure_z(const char* const& sz, std::ptrdiff_t max) +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline basic_string_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline basic_string_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } template -basic_string_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } +span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template -basic_string_span::type, dynamic_range> ensure_z(Cont& cont) +span::type, dynamic_range> ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline span remove_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + return{ sz, static_cast(len) }; +} + +inline span remove_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, max); + return{ sz, static_cast(len) }; +} + +template +span remove_z(T(&sz)[N]) +{ + return remove_z(&sz[0], static_cast(N)); +} + +template +span::type, dynamic_range> remove_z(Cont& cont) +{ + return remove_z(cont.data(), static_cast(cont.length())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + {}; + + template + struct is_basic_string_span_oracle> : std::true_type + {}; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + {}; +} + +// +// string_span and relatives +// +// Note that Extent is always single-dimension only +// +template +class basic_string_span +{ + using value_type = CharT; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t; + using bounds_type = static_bounds; + using underlying_type = span; + +public: + using size_type = ptrdiff_t; + using iterator = typename underlying_type::iterator; + using const_iterator = typename underlying_type::const_iterator; + using reverse_iterator = typename underlying_type::reverse_iterator; + using const_reverse_iterator = typename underlying_type::const_reverse_iterator; + + // empty + constexpr basic_string_span() noexcept + : real(nullptr) + {} + + // copy + constexpr basic_string_span(const basic_string_span& other) noexcept + : real(other.real) + {} + + // move + constexpr basic_string_span(const basic_string_span&& other) noexcept + : real(std::move(other.real)) + {} + + // from nullptr and length + constexpr basic_string_span(nullptr_t ptr, size_type length) noexcept + : real(ptr, length) + {} + + // For pointers and static arrays - if 0-terminated, remove 0 from the view + + // from c string + + constexpr basic_string_span(pointer& ptr) noexcept + : real(ensure_z(ptr)) + {} + + // from non-const pointer to const span + template::value>> + constexpr basic_string_span(std::remove_const_t*& ptr) noexcept + : real(ensure_z(ptr)) + {} + + // from raw data and length - remove 0 if needed + constexpr basic_string_span(pointer ptr, size_type length) noexcept + : real(remove_z(ptr, length)) + {} + + // from static arrays and string literals + template + constexpr basic_string_span(value_type(&arr)[N]) noexcept + : real(remove_z(arr)) + {} + + // Those allow 0s in the middle, so we keep them + + constexpr basic_string_span(std::string& s) noexcept + : real(&(s.at(0)), static_cast(s.length())) + {} + + // from containers. It must have .size() and .data() function signatures + template ::value + && !details::is_basic_string_span::value + && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + constexpr basic_string_span(Cont& cont) + : real(cont.data(), cont.size()) + {} + + // from span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(const span& other) noexcept + : real(other) + {} + + // from string_span + template , + typename Dummy = std::enable_if_t::value && std::is_convertible::value> + > + constexpr basic_string_span(const basic_string_span& other) noexcept + : real(other.data(), other.length()) + {} + + // section on linear space + template + constexpr basic_string_span first() const noexcept + { + return{ real.first() }; + } + + constexpr basic_string_span first(size_type count) const noexcept + { + return{ real.first(count); } + } + + template + constexpr basic_string_span last() const noexcept + { + return{ real.last() }; + } + + constexpr basic_string_span last(size_type count) const noexcept + { + return{ real.last(count); } + } + + template + constexpr basic_string_span sub() const noexcept + { + return{ real.sub() }; + } + + constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept + { + return{ real.sub(offset, count) }; + } + + constexpr const_reference operator[](size_type idx) const noexcept + { + return real[idx]; + } + + constexpr reference operator[](size_type idx) noexcept + { + return real[idx]; + } + + constexpr pointer data() const noexcept + { + return real.data(); + } + + constexpr size_type length() const noexcept + { + return real.size(); + } + + constexpr size_type size() const noexcept + { + return real.size(); + } + + constexpr size_type used_length() const noexcept + { + return length(); + } + + constexpr size_type bytes() const noexcept + { + return real.bytes(); + } + + constexpr size_type used_bytes() const noexcept + { + return bytes(); + } + + constexpr explicit operator bool() const noexcept + { + return real; + } + + constexpr iterator begin() const noexcept + { + return real.begin(); + } + + constexpr iterator end() const noexcept + { + return real.end(); + } + + constexpr const_iterator cbegin() const noexcept + { + return real.cbegin(); + } + + constexpr const_iterator cend() const noexcept + { + real.cend(); + } + + constexpr reverse_iterator rbegin() const noexcept + { + return real.rbegin(); + } + + constexpr reverse_iterator rend() const noexcept + { + return real.rend(); + } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return real.crbegin(); + } + + constexpr const_reverse_iterator crend() const noexcept + { + return real.crend(); + } + +private: + span real; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + // // to_string() allow (explicit) conversions from string_span to string // @@ -213,14 +509,89 @@ template using wzstring_builder = basic_zstring_builder; } + +constexpr bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + +constexpr bool operator==(const gsl::string_span<>& one, const gsl::string_span<>& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + + +// TODO: ca we make twmplate ops work? +//template +//constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +//{ +// return std::equal(one.begin(), one.end(), other.begin(), other.end()); +//} +/* +template , std::remove_cv_t>::value>> +constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} + +template , std::remove_cv_t>::value>> +constexpr bool operator!=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(one == other); +} + +template , std::remove_cv_t>::value>> +constexpr bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); +} + +template , std::remove_cv_t>::value>> +constexpr bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(other < one); +} + +template , std::remove_cv_t>::value>> +constexpr bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return other < one; +} + +template , std::remove_cv_t>::value>> +constexpr bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return !(one < other); +} +*/ // VS 2013 workarounds #ifdef _MSC_VER + +#undef constexpr +#pragma pop_macro("constexpr") + #if _MSC_VER <= 1800 -#undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#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 diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index f14df93..08faaa8 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include using namespace std; using namespace gsl; @@ -27,7 +29,7 @@ SUITE(string_span_tests) { TEST(TestLiteralConstruction) - { + { cwstring_span<> v = ensure_z(L"Hello"); CHECK(5 == v.length()); @@ -35,7 +37,7 @@ SUITE(string_span_tests) #ifdef CONFIRM_COMPILATION_ERRORS wstring_span<> v2 = ensure0(L"Hello"); #endif - } + } TEST(TestConstructFromStdString) { @@ -51,8 +53,8 @@ SUITE(string_span_tests) CHECK(v.length() == static_cast::size_type>(vec.size())); } - TEST(TestStackArrayConstruction) - { + TEST(TestStackArrayConstruction) + { wchar_t stack_string[] = L"Hello"; { @@ -62,7 +64,7 @@ SUITE(string_span_tests) { cwstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } { @@ -72,7 +74,7 @@ SUITE(string_span_tests) { wstring_span<> v = stack_string; - CHECK(v.length() == 6); + CHECK(v.length() == 5); } } @@ -95,7 +97,7 @@ SUITE(string_span_tests) { char stack_string[] = "Hello"; cstring_span<> v = ensure_z(stack_string); - (void)v; + (void)v; #ifdef CONFIRM_COMPILATION_ERRORS string_span<> v2 = v; string_span<> v3 = "Hello"; @@ -113,6 +115,391 @@ SUITE(string_span_tests) CHECK(static_cast::size_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); } + + 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + + // 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 of the original data to string + CHECK(span.data() == std::string("Hello")); + + CHECK(span == "Hello"); + CHECK(span == ar); + CHECK(span == ar1); + CHECK(span == ar2); + CHECK(span == ptr); + CHECK(span == str); + CHECK(span == vec); + + char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char _ar1[] = "Hello"; + char _ar2[10] = "Hello"; + char* _ptr = _ar1; + std::string _str = "Hello"; + std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; + + CHECK(span == _ar); + CHECK(span == _ar1); + CHECK(span == _ar2); + CHECK(span == _ptr); + CHECK(span == _str); + CHECK(span == _vec); + + string_span<> _span{ _ptr }; + + CHECK(_span == _ar); + CHECK(_span == _ar1); + CHECK(_span == _ar2); + CHECK(_span == _ptr); + CHECK(_span == _str); + CHECK(_span == _vec); + + CHECK(_span == "Hello"); + CHECK(_span == ar); + CHECK(_span == ar1); + CHECK(_span == ar2); + CHECK(_span == ptr); + CHECK(_span == str); + CHECK(_span == vec); + } + + { + std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span1 = str1; + std::vector str2 = std::move(str1); + cstring_span<> span2 = str2; + + // comparison of spans from the same vector before and after move (ok) + CHECK(span1 == span2); + } + } + + TEST(EnzureRemoveZ) + { + // 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(ptr); + CHECK(span.length() == 2); + + delete[] ptr; + } + + // ensuze z on c strings + { + char* ptr = new char[2]; + + ptr[0] = 'a'; + ptr[1] = 'b'; + + // do we want to have a constructor from pointer at all? + // the behavior is unpredictable if the string is not 0-terminated + + // CHECK_THROW((string_span<>(ptr).length() == 2), fail_fast); + + cstring_span<> sp1{ ptr, 2 }; // good + cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there + + CHECK(sp1[1] == 'b'); + CHECK_THROW((sp1[2] == 'c'), fail_fast); + + CHECK(sp2[1] == 'b'); + //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow + + delete[] ptr; + } + } + + TEST(Constructors) + { + // from string temporary +#ifdef CONFIRM_COMPILATION_ERRORS + { + cstring_span<> span = std::string("Hello"); + } +#endif + + // 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 non-const ptr and length + { + char* ptr = "Hello"; + 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + cstring_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + cstring_span<> span = inner; + CHECK(span.length() == 5); + } + + // from const string_span + { + std::vector 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> tmp = vec; + cstring_span<> span = tmp; + CHECK(span.length() == 5); + } + + /////////////////////////////////////////////////// + // How string_span should behave with const data + + // from string literal + { +#ifdef CONFIRM_COMPILATION_ERRORS + string_span<> span = "Hello"; + CHECK(span.length() == 5); +#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* ptr = "Hello"; + 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); +#endif + } + + // from non-const vector + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = vec; + CHECK(span.length() == 5); + } + + // from const span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from non-const span + { + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + span inner = vec; + string_span<> span = inner; + CHECK(span.length() == 5); + } + + // from non-const span of non-const data from const vector (looks like a bug) + { +#ifdef CONFIRM_COMPILATION_ERRORS + const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + const span inner = vec; // fix error (happens inside the constructor) + string_span<> span = inner; + CHECK(span.length() == 5); +#endif + } + + // from const string_span + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector 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 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 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + const string_span<> tmp = vec; // what does "const span" mean? + string_span<> span = tmp; + CHECK(span.length() == 5); + } + } + } int main(int, const char *[]) -- cgit v1.2.3 From 8c5d06dc791c7dea8148a5d3867473fab2e2fcf3 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 23 Nov 2015 21:24:08 +0000 Subject: fixed GCC and clang compilation issues --- include/string_span.h | 56 ++++++++++++++++++++++++++------------------- tests/string_span_tests.cpp | 8 ++++++- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b11dd9e..b681a5c 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -86,7 +86,7 @@ using wzstring = wchar_t*; // // Will fail-fast if sentinel cannot be found before max elements are examined. // -template +template span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; @@ -101,7 +101,7 @@ span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) // Will fail fast if a null-terminator cannot be found before // the limit of size_type. // -template +template inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); @@ -133,7 +133,7 @@ inline span ensure_z(const wchar_t* const& sz, std Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -template +template span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template @@ -168,7 +168,7 @@ inline span remove_z(const wchar_t* const& sz, std return{ sz, static_cast(len) }; } -template +template span remove_z(T(&sz)[N]) { return remove_z(&sz[0], static_cast(N)); @@ -203,7 +203,7 @@ namespace details // // Note that Extent is always single-dimension only // -template +template class basic_string_span { using value_type = CharT; @@ -237,7 +237,7 @@ public: {} // from nullptr and length - constexpr basic_string_span(nullptr_t ptr, size_type length) noexcept + constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept : real(ptr, length) {} @@ -250,8 +250,8 @@ public: {} // from non-const pointer to const span - template::value>> - constexpr basic_string_span(std::remove_const_t*& ptr) noexcept + template, bool Enabled = std::is_const::value, typename Dummy = std::enable_if_t> + constexpr basic_string_span(ValueType*& ptr) noexcept : real(ensure_z(ptr)) {} @@ -306,29 +306,29 @@ public: template constexpr basic_string_span first() const noexcept { - return{ real.first() }; + return{ real.template first() }; } constexpr basic_string_span first(size_type count) const noexcept { - return{ real.first(count); } + return{ real.first(count) }; } template constexpr basic_string_span last() const noexcept { - return{ real.last() }; + return{ real.template last() }; } constexpr basic_string_span last(size_type count) const noexcept { - return{ real.last(count); } + return{ real.last(count) }; } template constexpr basic_string_span sub() const noexcept { - return{ real.sub() }; + return{ real.template sub() }; } constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept @@ -442,7 +442,7 @@ using cwstring_span = basic_string_span; // #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -template +template std::basic_string::type> to_string(basic_string_span view) { return{ view.data(), static_cast(view.length()) }; @@ -472,7 +472,7 @@ inline std::wstring to_string(wstring_span<> view) #endif -template +template class basic_zstring_builder { public: @@ -509,24 +509,32 @@ template using wzstring_builder = basic_zstring_builder; } - -constexpr bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept +/* +bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } -constexpr bool operator==(const gsl::string_span<>& one, const gsl::string_span<>& other) noexcept +bool operator==(const gsl::cwstring_span<>& one, const gsl::cwstring_span<>& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } +*/ + +template +bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} -// TODO: ca we make twmplate ops work? -//template -//constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept -//{ -// return std::equal(one.begin(), one.end(), other.begin(), other.end()); -//} +/* +template , std::remove_cv_t>::value>> +constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +{ + return std::equal(one.begin(), one.end(), other.begin(), other.end()); +} +*/ /* template , std::remove_cv_t>::value>> constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 08faaa8..90b7c3c 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -248,7 +248,7 @@ SUITE(string_span_tests) cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there CHECK(sp1[1] == 'b'); - CHECK_THROW((sp1[2] == 'c'), fail_fast); + CHECK_THROW((void)(sp1[2] == 'c'), fail_fast); CHECK(sp2[1] == 'b'); //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow @@ -295,9 +295,12 @@ SUITE(string_span_tests) // from non-const ptr and length { + // does not compile with GCC (ISO standard does not allow converting string literals to char*) +#ifdef CONFIRM_COMPILATION_ERRORS char* ptr = "Hello"; cstring_span<> span{ ptr, 5 }; CHECK(span.length() == 5); +#endif } // from const string @@ -398,9 +401,12 @@ SUITE(string_span_tests) // from non-const ptr and length { + // does not compile with GCC (ISO standard does not allows converting string literals to char*) +#ifdef CONFIRM_COMPILATION_ERRORS char* ptr = "Hello"; string_span<> span{ ptr, 5 }; CHECK(span.length() == 5); +#endif } // from const string -- cgit v1.2.3 From 7077105b9de356b67695fe6b5d7ec0af2ec9e107 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 23 Nov 2015 23:05:31 -0800 Subject: Added operator== --- include/string_span.h | 338 ++++++++++++++++++++------------------------ tests/string_span_tests.cpp | 268 +++++++++++++++++++++++++++++------ 2 files changed, 376 insertions(+), 230 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b681a5c..b6dce6b 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -20,6 +20,7 @@ #define GSL_STRING_SPAN_H #include "gsl_assert.h" +#include "gsl_util.h" #include "span.h" #include @@ -32,12 +33,12 @@ // VS 2013 workarounds #if _MSC_VER <= 1800 -#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -// noexcept is not understood +// noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /* nothing */ #endif #endif // _MSC_VER <= 1800 @@ -50,16 +51,16 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ +#define noexcept /* nothing */ -#endif // GSL_THROW_ON_CONTRACT_VIOLATION +#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) +// 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 @@ -79,7 +80,7 @@ template using wzstring = wchar_t*; // -// ensure_sentinel() +// ensure_sentinel() // // Provides a way to obtain an span from a contiguous sequence // that ends with a (non-inclusive) sentinel value. @@ -97,7 +98,7 @@ span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) // -// ensure_z - creates a string_span for a czstring or cwzstring. +// 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. // @@ -118,19 +119,22 @@ inline span ensure_z(char* const& sz, std::ptrdiff_t max) inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; } inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, max); - Ensures(sz[len] == 0); return{ sz, static_cast(len) }; + Ensures(sz[len] == 0); + return{ sz, static_cast(len) }; } template @@ -142,45 +146,7 @@ span::type, dynamic_range> return ensure_z(cont.data(), static_cast(cont.length())); } - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline span remove_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - return{ sz, static_cast(len) }; -} - -inline span remove_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, max); - return{ sz, static_cast(len) }; -} - -template -span remove_z(T(&sz)[N]) -{ - return remove_z(&sz[0], static_cast(N)); -} - -template -span::type, dynamic_range> remove_z(Cont& cont) -{ - return remove_z(cont.data(), static_cast(cont.length())); -} - -template +template class basic_string_span; namespace details @@ -189,15 +155,56 @@ namespace details struct is_basic_string_span_oracle : std::false_type {}; - template - struct is_basic_string_span_oracle> : std::true_type + template + struct is_basic_string_span_oracle> : std::true_type {}; template struct is_basic_string_span : is_basic_string_span_oracle> {}; + + template + struct length_func + {}; + + template <> + struct length_func + { + std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, length)); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, length)); + } + }; } + // // string_span and relatives // @@ -212,85 +219,83 @@ class basic_string_span using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t; using bounds_type = static_bounds; - using underlying_type = span; + using impl_type = span; public: using size_type = ptrdiff_t; - using iterator = typename underlying_type::iterator; - using const_iterator = typename underlying_type::const_iterator; - using reverse_iterator = typename underlying_type::reverse_iterator; - using const_reverse_iterator = typename underlying_type::const_reverse_iterator; - - // empty - constexpr basic_string_span() noexcept - : real(nullptr) - {} + 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) noexcept - : real(other.real) - {} + constexpr basic_string_span(const basic_string_span& other) = default; // move - constexpr basic_string_span(const basic_string_span&& other) noexcept - : real(std::move(other.real)) - {} + 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 and length constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept - : real(ptr, length) + : span_(ptr, length) {} // For pointers and static arrays - if 0-terminated, remove 0 from the view - // from c string - - constexpr basic_string_span(pointer& ptr) noexcept - : real(ensure_z(ptr)) - {} - - // from non-const pointer to const span - template, bool Enabled = std::is_const::value, typename Dummy = std::enable_if_t> - constexpr basic_string_span(ValueType*& ptr) noexcept - : real(ensure_z(ptr)) - {} - - // from raw data and length - remove 0 if needed + // from raw data and length constexpr basic_string_span(pointer ptr, size_type length) noexcept - : real(remove_z(ptr, length)) + : span_(remove_z(ptr, length)) {} // from static arrays and string literals template constexpr basic_string_span(value_type(&arr)[N]) noexcept - : real(remove_z(arr)) + : span_(remove_z(arr)) {} - // Those allow 0s in the middle, so we keep them + // Those allow 0s within the length, so we do not remove them + // from string constexpr basic_string_span(std::string& s) noexcept - : real(&(s.at(0)), static_cast(s.length())) + : span_(&(s.at(0)), narrow_cast(s.length())) {} - // from containers. It must have .size() and .data() function signatures + // from containers. Containers must have .size() and .data() function signatures template ::value - && !details::is_basic_string_span::value + && !details::is_basic_string_span::value && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> > constexpr basic_string_span(Cont& cont) - : real(cont.data(), cont.size()) + : span_(cont.data(), cont.size()) {} + // disallow creation from temporary containers and strings + template ::value + && !details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + basic_string_span(Cont&& cont) = delete; + // from span template , typename Dummy = std::enable_if_t::value && std::is_convertible::value> > - constexpr basic_string_span(const span& other) noexcept - : real(other) + constexpr basic_string_span(span other) noexcept + : span_(other) {} // from string_span @@ -298,131 +303,130 @@ public: typename OtherBounds = static_bounds, typename Dummy = std::enable_if_t::value && std::is_convertible::value> > - constexpr basic_string_span(const basic_string_span& other) noexcept - : real(other.data(), other.length()) + constexpr basic_string_span(basic_string_span other) noexcept + : span_(other.data(), other.length()) {} - // section on linear space + constexpr bool empty() const noexcept + { + return length() == 0; + } + + // first Count elements template constexpr basic_string_span first() const noexcept { - return{ real.template first() }; + return{ span_.template first() }; } constexpr basic_string_span first(size_type count) const noexcept { - return{ real.first(count) }; + return{ span_.first(count) }; } + // last Count elements template constexpr basic_string_span last() const noexcept { - return{ real.template last() }; + return{ span_.template last() }; } constexpr basic_string_span last(size_type count) const noexcept { - return{ real.last(count) }; + return{ span_.last(count) }; } + // Count elements starting from Offset template constexpr basic_string_span sub() const noexcept { - return{ real.template sub() }; + return{ span_.template sub() }; } constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept { - return{ real.sub(offset, count) }; + return{ span_.sub(offset, count) }; } - constexpr const_reference operator[](size_type idx) const noexcept + constexpr reference operator[](size_type idx) const noexcept { - return real[idx]; - } - - constexpr reference operator[](size_type idx) noexcept - { - return real[idx]; + return span_[idx]; } constexpr pointer data() const noexcept { - return real.data(); + return span_.data(); } constexpr size_type length() const noexcept { - return real.size(); + return span_.size(); } constexpr size_type size() const noexcept { - return real.size(); - } - - constexpr size_type used_length() const noexcept - { - return length(); + return span_.size(); } constexpr size_type bytes() const noexcept { - return real.bytes(); - } - - constexpr size_type used_bytes() const noexcept - { - return bytes(); - } - - constexpr explicit operator bool() const noexcept - { - return real; + return span_.bytes(); } constexpr iterator begin() const noexcept { - return real.begin(); + return span_.begin(); } constexpr iterator end() const noexcept { - return real.end(); + return span_.end(); } constexpr const_iterator cbegin() const noexcept { - return real.cbegin(); + return span_.cbegin(); } constexpr const_iterator cend() const noexcept { - real.cend(); + span_.cend(); } constexpr reverse_iterator rbegin() const noexcept { - return real.rbegin(); + return span_.rbegin(); } constexpr reverse_iterator rend() const noexcept { - return real.rend(); + return span_.rend(); } constexpr const_reverse_iterator crbegin() const noexcept { - return real.crbegin(); + return span_.crbegin(); } constexpr const_reverse_iterator crend() const noexcept { - return real.crend(); + return span_.crend(); } private: - span real; + + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) noexcept + { + return{ sz, details::length_func()(sz, max)}; + } + + template + static impl_type remove_z(value_type(&sz)[N]) noexcept + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; }; template @@ -440,7 +444,7 @@ using cwstring_span = basic_string_span; // // to_string() allow (explicit) conversions from string_span to string // -#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG template std::basic_string::type> to_string(basic_string_span view) @@ -470,12 +474,13 @@ inline std::wstring to_string(wstring_span<> view) return{ view.data(), view.length() }; } -#endif +#endif template class basic_zstring_builder { public: + using impl_type = span; using string_span_type = basic_string_span; using value_type = CharT; using pointer = CharT*; @@ -499,7 +504,7 @@ public: iterator end() const { return sv_.end(); } private: - string_span_type sv_; + impl_type sv_; }; template @@ -509,69 +514,36 @@ template using wzstring_builder = basic_zstring_builder; } -/* -bool operator==(const gsl::cstring_span<>& one, const gsl::cstring_span<>& other) noexcept -{ - return std::equal(one.begin(), one.end(), other.begin(), other.end()); -} - -bool operator==(const gsl::cwstring_span<>& one, const gsl::cwstring_span<>& other) noexcept -{ - return std::equal(one.begin(), one.end(), other.begin(), other.end()); -} -*/ - - -template -bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return std::equal(one.begin(), one.end(), other.begin(), other.end()); } -/* -template , std::remove_cv_t>::value>> -constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept -{ - return std::equal(one.begin(), one.end(), other.begin(), other.end()); -} -*/ -/* -template , std::remove_cv_t>::value>> -constexpr bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept -{ - return std::equal(one.begin(), one.end(), other.begin(), other.end()); -} - -template , std::remove_cv_t>::value>> -constexpr bool operator!=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept -{ - return !(one == other); -} - -template , std::remove_cv_t>::value>> -constexpr bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); } -template , std::remove_cv_t>::value>> -constexpr bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(other < one); } -template , std::remove_cv_t>::value>> -constexpr bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return other < one; } -template , std::remove_cv_t>::value>> -constexpr bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template +bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { return !(one < other); } -*/ + // VS 2013 workarounds #ifdef _MSC_VER @@ -592,7 +564,7 @@ constexpr bool operator>=(const gsl::basic_string_span& one, #endif // _MSC_VER <= 1800 #endif // _MSC_VER -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #undef noexcept diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 90b7c3c..4e42cc5 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -14,12 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// -#include +#include #include #include #include -#include -#include using namespace std; using namespace gsl; @@ -76,7 +74,7 @@ SUITE(string_span_tests) wstring_span<> v = stack_string; CHECK(v.length() == 5); } - } + } TEST(TestConstructFromConstCharPointer) { @@ -89,7 +87,7 @@ SUITE(string_span_tests) { char stack_string[] = "Hello"; string_span<> v = ensure_z(stack_string); - cstring_span<> v2 = v; + cstring_span<> v2 = v; CHECK(v.length() == v2.length()); } @@ -116,7 +114,7 @@ SUITE(string_span_tests) CHECK(s2.length() == 5); } - TEST(ComparisonAndImplicitConstructors) + TEST(EqualityAndImplicitConstructors) { { cstring_span<> span = "Hello"; @@ -132,25 +130,60 @@ SUITE(string_span_tests) CHECK(span == cstring_span<>("Hello")); // comparison to static array with no null termination - CHECK(span == cstring_span<>(ar)); + 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)); + CHECK(span == cstring_span<>(ar2)); // comparison to null-terminated c string - CHECK(span == cstring_span<>(ptr, 5)); + CHECK(span == cstring_span<>(ptr, 5)); // comparison to string - CHECK(span == cstring_span<>(str)); + CHECK(span == cstring_span<>(str)); // comparison to vector of charaters with no null termination - CHECK(span == cstring_span<>(vec)); + CHECK(span == cstring_span<>(vec)); // 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + + // 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)); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + cstring_span<> span = "Hello"; CHECK(span == "Hello"); CHECK(span == ar); @@ -159,7 +192,7 @@ SUITE(string_span_tests) CHECK(span == ptr); CHECK(span == str); CHECK(span == vec); - + char _ar[] = { 'H', 'e', 'l', 'l', 'o' }; char _ar1[] = "Hello"; char _ar2[10] = "Hello"; @@ -191,6 +224,7 @@ SUITE(string_span_tests) CHECK(_span == str); CHECK(_span == vec); } +#endif { std::vector str1 = { 'H', 'e', 'l', 'l', 'o' }; @@ -203,6 +237,78 @@ SUITE(string_span_tests) } } + + 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 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 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(EnzureRemoveZ) { // remove z from literals @@ -226,39 +332,17 @@ SUITE(string_span_tests) ptr[1] = 'b'; ptr[2] = '\0'; - string_span<> span(ptr); + string_span<> span = ensure_z(ptr); CHECK(span.length() == 2); delete[] ptr; } - - // ensuze z on c strings - { - char* ptr = new char[2]; - - ptr[0] = 'a'; - ptr[1] = 'b'; - - // do we want to have a constructor from pointer at all? - // the behavior is unpredictable if the string is not 0-terminated - - // CHECK_THROW((string_span<>(ptr).length() == 2), fail_fast); - - cstring_span<> sp1{ ptr, 2 }; // good - cstring_span<> sp2{ ptr, 3 }; // bad... but can't help there - - CHECK(sp1[1] == 'b'); - CHECK_THROW((void)(sp1[2] == 'c'), fail_fast); - - CHECK(sp2[1] == 'b'); - //CHECK_THROW((sp1[2] == 'c'), fail_fast); // buffer overflow - - delete[] ptr; - } } TEST(Constructors) { + // creating cstring_span + // from string temporary #ifdef CONFIRM_COMPILATION_ERRORS { @@ -363,14 +447,12 @@ SUITE(string_span_tests) CHECK(span.length() == 5); } - /////////////////////////////////////////////////// - // How string_span should behave with const data + // creating string_span // from string literal { #ifdef CONFIRM_COMPILATION_ERRORS string_span<> span = "Hello"; - CHECK(span.length() == 5); #endif } @@ -401,12 +483,10 @@ SUITE(string_span_tests) // from non-const ptr and length { - // does not compile with GCC (ISO standard does not allows converting string literals to char*) -#ifdef CONFIRM_COMPILATION_ERRORS - char* ptr = "Hello"; + char ar[] = { 'H', 'e', 'l', 'l', 'o' }; + char* ptr = ar; string_span<> span{ ptr, 5 }; CHECK(span.length() == 5); -#endif } // from const string @@ -459,11 +539,11 @@ SUITE(string_span_tests) CHECK(span.length() == 5); } - // from non-const span of non-const data from const vector (looks like a bug) + // from non-const span of non-const data from const vector { #ifdef CONFIRM_COMPILATION_ERRORS const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const span inner = vec; // fix error (happens inside the constructor) + const span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -500,12 +580,106 @@ SUITE(string_span_tests) // from const string_span of non-const data { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const string_span<> tmp = vec; // what does "const span" mean? + const string_span<> tmp = vec; string_span<> span = tmp; CHECK(span.length() == 5); } } + template + T move_wrapper(T&& t) + { + return std::move(t); + } + + template + T create() { return T{}; } + + template + void use(basic_string_span 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 span = ensure_z("Hello"); + cstring_span<> span1 = std::move(span); + CHECK(span1.length() == 5); + } + { + span 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::move(str)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create()); +#endif + } + + // move container + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = std::move(vec); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + string_span<> span = move_wrapper>(std::move(vec)); + CHECK(span.length() == 5); +#endif + } + { +#ifdef CONFIRM_COMPILATION_ERRORS + use(create>()); +#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 *[]) -- cgit v1.2.3 From de6dbacbf6debb96297f4c554473069b8a3edb4c Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Tue, 1 Dec 2015 13:52:01 -0800 Subject: Addressed CR comments --- include/string_span.h | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index b6dce6b..cc87068 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -336,16 +336,16 @@ public: return{ span_.last(count) }; } - // Count elements starting from Offset + // create a subview of Count elements starting from Offset template - constexpr basic_string_span sub() const noexcept + constexpr basic_string_span subspan() const noexcept { - return{ span_.template sub() }; + return{ span_.template subspan() }; } - constexpr basic_string_span sub(size_type offset, size_type count = dynamic_range) const noexcept + constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_range) const noexcept { - return{ span_.sub(offset, count) }; + return{ span_.subspan(offset, count) }; } constexpr reference operator[](size_type idx) const noexcept @@ -358,19 +358,28 @@ public: 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(); } - constexpr size_type bytes() const noexcept + // length of the span in bytes + constexpr size_type size_bytes() const noexcept { - return span_.bytes(); + 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 -- cgit v1.2.3 From 4efa9e8f07fbe5799444c8464bd6061403dce9ef Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 2 Dec 2015 12:28:43 -0800 Subject: Run clang format --- tests/string_span_tests.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 4e42cc5..a966955 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -1,18 +1,19 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// /////////////////////////////////////////////////////////////////////////////// +// +// 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 #include -- cgit v1.2.3 From eedd18d5b8208a096bff2790a161ebb5f863defd Mon Sep 17 00:00:00 2001 From: mcheese Date: Sun, 6 Dec 2015 13:43:03 +0100 Subject: Correct missing rename of bytes() to size_bytes() --- include/span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/span.h b/include/span.h index 8151737..72d55d6 100644 --- a/include/span.h +++ b/include/span.h @@ -1669,7 +1669,7 @@ constexpr auto as_span(span s) noexcept -> span< ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), "Target type must be a trivial type and its size must match the byte array size"); - Expects((s.bytes() % sizeof(U)) == 0); + Expects((s.size_bytes() % sizeof(U)) == 0); return {reinterpret_cast(s.data()), s.size_bytes() / narrow_cast(sizeof(U))}; } -- cgit v1.2.3 From 87c5daa6c4c00d7660e5343d8d1dd13e300390a5 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 4 Dec 2015 14:23:13 -0800 Subject: Fixed operators and constructors for string_span --- include/string_span.h | 161 +++++++++++++++++++++++++++++++++++--------- tests/string_span_tests.cpp | 143 +++++++++++++++++++++++++++++++++++---- 2 files changed, 261 insertions(+), 43 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index cc87068..8418029 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -243,17 +243,17 @@ public: // 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) {} - // For pointers and static arrays - if 0-terminated, remove 0 from the view - - // from raw data and length - constexpr basic_string_span(pointer ptr, size_type length) noexcept - : span_(remove_z(ptr, length)) - {} + // From static arrays - if 0-terminated, remove 0 from the view // from static arrays and string literals template @@ -263,6 +263,11 @@ public: // 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_(&(s.at(0)), narrow_cast(s.length())) @@ -523,32 +528,126 @@ template using wzstring_builder = basic_zstring_builder; } -template -bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +// operator == +template , Extent>>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +// operator != +template , Extent>>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept { - return std::equal(one.begin(), one.end(), other.begin(), other.end()); + return !(one == other); } -template -bool operator<(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +// operator< +template , Extent>>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept { + gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); } -template -bool operator<=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +// operator <= +template , Extent>>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept { return !(other < one); } -template -bool operator>(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +// operator> +template , Extent>>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept { return other < one; } -template -bool operator>=(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept +// operator >= +template , Extent>>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template , Extent>>::value + && !details::is_basic_string_span::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); } diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index a966955..18fda26 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -14,11 +14,10 @@ // /////////////////////////////////////////////////////////////////////////////// - #include +#include #include #include -#include using namespace std; using namespace gsl; @@ -126,6 +125,7 @@ SUITE(string_span_tests) const char* ptr = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z("Hello"); // comparison to literal CHECK(span == cstring_span<>("Hello")); @@ -148,6 +148,12 @@ SUITE(string_span_tests) // 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")); } @@ -162,6 +168,7 @@ SUITE(string_span_tests) char* ptr = ar; std::string str = "Hello"; std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span sp = ensure_z(ar1); // comparison to static array with no null termination CHECK(span == string_span<>(ar)); @@ -180,52 +187,133 @@ SUITE(string_span_tests) // 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); } -#ifdef CONFIRM_COMPILATION_ERRORS + { + 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 vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span 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 = _ar1; + char* _ptr = _ar; std::string _str = "Hello"; std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; + gsl::span _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); - string_span<> _span{ _ptr }; + 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 str1 = { 'H', 'e', 'l', 'l', 'o' }; @@ -238,7 +326,6 @@ SUITE(string_span_tests) } } - TEST(ComparisonAndImplicitConstructors) { { @@ -310,7 +397,7 @@ SUITE(string_span_tests) CHECK(span >= string_span<>(vec)); } } - TEST(EnzureRemoveZ) + TEST(ConstrutorsEnsureZ) { // remove z from literals { @@ -351,6 +438,18 @@ SUITE(string_span_tests) } #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"; @@ -378,14 +477,34 @@ SUITE(string_span_tests) 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 { - // does not compile with GCC (ISO standard does not allow converting string literals to char*) -#ifdef CONFIRM_COMPILATION_ERRORS - char* ptr = "Hello"; + 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); -#endif } // from const string -- cgit v1.2.3 From c6f3579ad1502015e320facf882f54caefbc266b Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Sat, 5 Dec 2015 01:03:19 +0000 Subject: Changes for gcc and clang --- include/string_span.h | 212 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 204 insertions(+), 8 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 8418029..6b0b32b 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -213,6 +213,7 @@ namespace details template class basic_string_span { +public: using value_type = CharT; using const_value_type = std::add_const_t; using pointer = std::add_pointer_t; @@ -221,7 +222,6 @@ class basic_string_span using bounds_type = static_bounds; using impl_type = span; -public: using size_type = ptrdiff_t; using iterator = typename impl_type::iterator; using const_iterator = typename impl_type::const_iterator; @@ -542,13 +542,47 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> +> +bool operator==(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +} + +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator==(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> > bool operator==(const T& one, gsl::basic_string_span other) noexcept { gsl::basic_string_span, Extent> tmp(one); return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); } +#endif // operator != template one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> +> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} + +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> > bool operator!=(const T& one, gsl::basic_string_span other) noexcept { return !(one == other); } +#endif // operator< template one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), other.begin(), other.end()); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator<(const T& one, gsl::basic_string_span other) noexcept { @@ -592,6 +658,40 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + // operator <= template one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> +> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} + +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> > bool operator<=(const T& one, gsl::basic_string_span other) noexcept { return !(other < one); } +#endif // operator> template (gsl::basic_string_span one, const T& other) noexce template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator>(const T& one, gsl::basic_string_span other) noexcept { return other < one; } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} +#endif + // operator >= template =(gsl::basic_string_span one, const T& other) noexc template , Extent>>::value - && !details::is_basic_string_span::value> + && !gsl::details::is_basic_string_span::value> > bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); } +#ifndef _MSC_VER + +// VS allows temp and const containers as convertible to basic_string_span, +// to the cases below are already by the revious operators + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template ::value + && !gsl::details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> +> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} +#endif + // VS 2013 workarounds #ifdef _MSC_VER -- cgit v1.2.3 From e19f6b5f362aac677b369b9e2b46010df13cf944 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 7 Dec 2015 15:22:11 -0800 Subject: Fixed a bug on creating a span from empty string --- include/string_span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_span.h b/include/string_span.h index 6b0b32b..d3419ee 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -270,7 +270,7 @@ public: // from string constexpr basic_string_span(std::string& s) noexcept - : span_(&(s.at(0)), narrow_cast(s.length())) + : span_(const_cast(s.data()), narrow_cast(s.length())) {} // from containers. Containers must have .size() and .data() function signatures -- cgit v1.2.3 From 5926942cd7b8270dc851297b27e995bc5d32ab7d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 7 Dec 2015 15:30:00 -0800 Subject: Tyding up comments --- include/string_span.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index d3419ee..a451314 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -552,8 +552,8 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexce #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template (const T& one, gsl::basic_string_span other) noexce #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template =(const T& one, gsl::basic_string_span other) noexc #ifndef _MSC_VER -// VS allows temp and const containers as convertible to basic_string_span, -// to the cases below are already by the revious operators +// VS treats temp and const containers as convertible to basic_string_span, +// so the cases below are already covered by the previous operators template Date: Wed, 9 Dec 2015 13:59:32 -0800 Subject: Add casts to eliminate signed/unsigned mismatch warnings Addresses issue #210 and VC's warning C4365 --- include/string_span.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index a451314..feb5ac6 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -111,28 +111,28 @@ inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDI // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation inline span ensure_z(char* const& sz, std::ptrdiff_t max) { - auto len = strnlen(sz, max); + auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { - auto len = strnlen(sz, max); + auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { - auto len = wcsnlen(sz, max); + auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { - auto len = wcsnlen(sz, max); + auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } @@ -172,7 +172,7 @@ namespace details { std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(strnlen(ptr, length)); + return narrow_cast(strnlen(ptr, narrow_cast(length))); } }; @@ -181,7 +181,7 @@ namespace details { std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(wcsnlen(ptr, length)); + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); } }; @@ -190,7 +190,7 @@ namespace details { std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(strnlen(ptr, length)); + return narrow_cast(strnlen(ptr, narrow_cast(length))); } }; @@ -199,7 +199,7 @@ namespace details { std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(wcsnlen(ptr, length)); + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); } }; } -- cgit v1.2.3 From 37cdb6bc50848e031ebe6a2291d953c178da7954 Mon Sep 17 00:00:00 2001 From: Pascal Menuet Date: Fri, 11 Dec 2015 20:47:07 +0100 Subject: Disable macro max in Windows. Implement move ctors in string_span for VS2013. Remove redundant pragma warning pop for VS2013. --- include/span.h | 11 +++++++++++ include/string_span.h | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index 8151737..7bb9c18 100644 --- a/include/span.h +++ b/include/span.h @@ -45,6 +45,12 @@ #pragma push_macro("constexpr") #define constexpr +// On Windows, if NOMINMAX is not defined, then windows.h defines the macro max +#ifdef _WIN32 +#pragma push_macro("max") +#define max max +#endif + // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -2196,6 +2202,11 @@ general_span_iterator operator+(typename general_span_iterator::diff #undef constexpr #pragma pop_macro("constexpr") +#ifdef _WIN32 +#undef max +#pragma pop_macro("max") +#endif + #if _MSC_VER <= 1800 #pragma warning(pop) diff --git a/include/string_span.h b/include/string_span.h index cc87068..6a71ce9 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -235,13 +235,28 @@ public: constexpr basic_string_span(const basic_string_span& other) = default; // move +#ifdef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr basic_string_span(basic_string_span&& other) + : span_(std::move(other.span_)) + { + } +#else constexpr basic_string_span(basic_string_span&& other) = default; +#endif // assign constexpr basic_string_span& operator=(const basic_string_span& other) = default; // move assign +#ifdef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr basic_string_span& operator=(basic_string_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#else constexpr basic_string_span& operator=(basic_string_span&& other) = default; +#endif // from nullptr and length constexpr basic_string_span(std::nullptr_t ptr, size_type length) noexcept @@ -561,8 +576,6 @@ bool operator>=(const gsl::basic_string_span& one, const gsl::bas #if _MSC_VER <= 1800 -#pragma warning(pop) - #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #undef noexcept #pragma pop_macro("noexcept") -- cgit v1.2.3 From 5e7e68c8b042d5b87b3b2a47999aa3665e1fb7f9 Mon Sep 17 00:00:00 2001 From: Pascal Menuet Date: Sat, 12 Dec 2015 22:51:49 +0100 Subject: For VS2013, fix an ICE by replacing dummy template type parameter by a dummy function parameter --- include/string_span.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/include/string_span.h b/include/string_span.h index 6a71ce9..01c56dc 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -34,6 +34,7 @@ #if _MSC_VER <= 1800 #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION @@ -283,6 +284,74 @@ public: : span_(&(s.at(0)), narrow_cast(s.length())) {} +#ifdef GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER + template< + typename Cont, + typename DataType = typename Cont::value_type + > + constexpr basic_string_span( + Cont& cont, + std::enable_if_t< + !details::is_span::value + && !details::is_basic_string_span::value + && !(!std::is_const::value && std::is_const::value) + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value + >* = nullptr + ) + : span_(cont.data(), cont.size()) + {} + + // disallow creation from temporary containers and strings + template< + typename Cont, + typename DataType = typename Cont::value_type + > + explicit basic_string_span( + Cont&& cont + , + std::enable_if_t< + !details::is_span::value + && !details::is_basic_string_span::value + && std::is_convertible::value + && std::is_same().size(), *std::declval().data())>, DataType>::value + >* = nullptr + ) = delete; + + // from span + template< + typename OtherValueType, + std::ptrdiff_t... OtherDimensions, + typename OtherBounds = static_bounds + > + constexpr basic_string_span( + span other, + typename std::enable_if< + std::is_convertible::value && + std::is_convertible::value + >::type* = nullptr + ) noexcept + : span_(other) + {} + + // from string_span + template< + typename OtherValueType, + std::ptrdiff_t OtherExtent, + typename OtherBounds = static_bounds + > + constexpr basic_string_span( + basic_string_span other, + std::enable_if_t< + std::is_convertible::value + && std::is_convertible::value + >* = nullptr + ) noexcept + : span_(other.data(), other.length()) + {} + +#else // GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER + // from containers. Containers must have .size() and .data() function signatures template ::value @@ -321,6 +390,7 @@ public: constexpr basic_string_span(basic_string_span other) noexcept : span_(other.data(), other.length()) {} +#endif // GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER constexpr bool empty() const noexcept { @@ -581,6 +651,7 @@ bool operator>=(const gsl::basic_string_span& one, const gsl::bas #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION +#undef GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #endif // _MSC_VER <= 1800 -- cgit v1.2.3 From f38ee4fb5e58b6bb88b723578661e1ad3744e3a2 Mon Sep 17 00:00:00 2001 From: Pascal Menuet Date: Sat, 12 Dec 2015 22:59:57 +0100 Subject: In VS2013, the std::equal does not have the overload that takes 4 iterators --- include/string_span.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/string_span.h b/include/string_span.h index 01c56dc..03ef07a 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -35,6 +35,7 @@ #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #define GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER +#define GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION @@ -611,7 +612,13 @@ using wzstring_builder = basic_zstring_builder; template bool operator==(const gsl::basic_string_span& one, const gsl::basic_string_span& other) noexcept { +#ifdef GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 + if (std::distance(one.begin(), one.end()) != std::distance(other.begin(), other.end())) + return false; + return std::equal(one.begin(), one.end(), other.begin()); +#else return std::equal(one.begin(), one.end(), other.begin(), other.end()); +#endif } template @@ -651,6 +658,7 @@ bool operator>=(const gsl::basic_string_span& one, const gsl::bas #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION +#undef GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 #undef GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -- cgit v1.2.3 From eb05256ffe1e76e47d53912eafe223387d0526f3 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Mon, 14 Dec 2015 11:36:20 -0800 Subject: Fixed compilation issues in MSVC 2013 --- include/string_span.h | 142 +++++++++++++------------------------------- tests/string_span_tests.cpp | 40 +++++++++++-- 2 files changed, 76 insertions(+), 106 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 44a3f56..c9a4dc9 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -34,8 +34,9 @@ #if _MSC_VER <= 1800 #define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -#define GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER -#define GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 +#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#define GSL_MSVC_NO_CPP14_STD_EQUAL +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION @@ -237,27 +238,26 @@ public: constexpr basic_string_span(const basic_string_span& other) = default; // move -#ifdef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr basic_string_span(basic_string_span&& other) - : span_(std::move(other.span_)) - { - } -#else +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_string_span(basic_string_span&& other) = default; +#else + constexpr basic_string_span(basic_string_span&& other) + : span_(std::move(other.span_)) + {} #endif // assign constexpr basic_string_span& operator=(const basic_string_span& other) = default; // move assign -#ifdef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr basic_string_span& operator=(basic_string_span&& other) - { - span_ = std::move(other.span_); - return *this; - } -#else +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_string_span& operator=(basic_string_span&& other) = default; +#else + constexpr basic_string_span& operator=(basic_string_span&& other) + { + span_ = std::move(other.span_); + return *this; + } #endif // from nullptr @@ -290,74 +290,6 @@ public: : span_(const_cast(s.data()), narrow_cast(s.length())) {} -#ifdef GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER - template< - typename Cont, - typename DataType = typename Cont::value_type - > - constexpr basic_string_span( - Cont& cont, - std::enable_if_t< - !details::is_span::value - && !details::is_basic_string_span::value - && !(!std::is_const::value && std::is_const::value) - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value - >* = nullptr - ) - : span_(cont.data(), cont.size()) - {} - - // disallow creation from temporary containers and strings - template< - typename Cont, - typename DataType = typename Cont::value_type - > - explicit basic_string_span( - Cont&& cont - , - std::enable_if_t< - !details::is_span::value - && !details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value - >* = nullptr - ) = delete; - - // from span - template< - typename OtherValueType, - std::ptrdiff_t... OtherDimensions, - typename OtherBounds = static_bounds - > - constexpr basic_string_span( - span other, - typename std::enable_if< - std::is_convertible::value && - std::is_convertible::value - >::type* = nullptr - ) noexcept - : span_(other) - {} - - // from string_span - template< - typename OtherValueType, - std::ptrdiff_t OtherExtent, - typename OtherBounds = static_bounds - > - constexpr basic_string_span( - basic_string_span other, - std::enable_if_t< - std::is_convertible::value - && std::is_convertible::value - >* = nullptr - ) noexcept - : span_(other.data(), other.length()) - {} - -#else // GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER - // from containers. Containers must have .size() and .data() function signatures template ::value @@ -379,14 +311,27 @@ public: > basic_string_span(Cont&& cont) = delete; +#ifndef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE // from span template , - typename Dummy = std::enable_if_t::value && std::is_convertible::value> + typename Dummy = std::enable_if_t< + std::is_convertible::value + && std::is_convertible, bounds_type>::value> > constexpr basic_string_span(span other) noexcept : span_(other) {} +#else + // from span + constexpr basic_string_span(span other) noexcept + : span_(other) + {} + + template , value_type>::value>> + constexpr basic_string_span(span, Extent> other) noexcept + : span_(other) + {} +#endif // from string_span template other) noexcept : span_(other.data(), other.length()) {} -#endif // GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER constexpr bool empty() const noexcept { @@ -556,22 +500,22 @@ std::basic_string::type> to_string(basic_strin inline std::string to_string(cstring_span<> view) { - return{ view.data(), view.length() }; + return{ view.data(), static_cast(view.length()) }; } inline std::string to_string(string_span<> view) { - return{ view.data(), view.length() }; + return{ view.data(), static_cast(view.length()) }; } inline std::wstring to_string(cwstring_span<> view) { - return{ view.data(), view.length() }; + return{ view.data(), static_cast(view.length()) }; } inline std::wstring to_string(wstring_span<> view) { - return{ view.data(), view.length() }; + return{ view.data(), static_cast(view.length()) }; } #endif @@ -622,10 +566,8 @@ template one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); -#ifdef GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 - if (std::distance(one.begin(), one.end()) != std::distance(tmp.begin(), tmp.end())) - return false; - return std::equal(one.begin(), one.end(), tmp.begin()); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); #else return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); #endif @@ -639,10 +581,8 @@ template other) noexcept { gsl::basic_string_span, Extent> tmp(one); -#ifdef GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 - if (std::distance(tmp.begin(), tmp.end()) != std::distance(other.begin(), other.end())) - return false; - return std::equal(tmp.begin(), tmp.end(), other.begin()); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); #else return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); #endif @@ -959,9 +899,10 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION -#undef GSL_MSVC2013_EQUAL_ALGORITHM_IS_NOT_CPP14 -#undef GSL_MSVC2013_ICE_WHEN_USING_DUMMY_TEMPLATE_PARAMETER #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#define GSL_MSVC_NO_CPP14_STD_EQUAL +#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR #endif // _MSC_VER <= 1800 #endif // _MSC_VER @@ -975,5 +916,4 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc #endif #endif // GSL_THROW_ON_CONTRACT_VIOLATION - #endif // GSL_STRING_SPAN_H diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 18fda26..88d125a 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -29,7 +29,6 @@ SUITE(string_span_tests) TEST(TestLiteralConstruction) { cwstring_span<> v = ensure_z(L"Hello"); - CHECK(5 == v.length()); #ifdef CONFIRM_COMPILATION_ERRORS @@ -116,6 +115,24 @@ SUITE(string_span_tests) TEST(EqualityAndImplicitConstructors) { + { + cstring_span<> span = "Hello"; + cstring_span<> span1; + + // comparison to empty span + CHECK(span1 != span); + CHECK(span != span1); + } + + { + cstring_span<> span = "Hello"; + cstring_span<> span1 = "Hello1"; + + // comparison to different span + CHECK(span1 != span); + CHECK(span != span1); + } + { cstring_span<> span = "Hello"; @@ -153,9 +170,6 @@ SUITE(string_span_tests) // comparison to string_span CHECK(span == span); - - // comparison of the original data to string - CHECK(span.data() == std::string("Hello")); } { @@ -431,6 +445,22 @@ SUITE(string_span_tests) { // creating cstring_span + // from span of a final extent + { + span sp = "Hello"; + cstring_span<> span = sp; + CHECK(span.length() == 6); + } + + // from const span of a final extent to non-const string_span +#ifdef CONFIRM_COMPILATION_ERRORS + { + span sp = "Hello"; + string_span<> span = sp; + CHECK(span.length() == 6); + } +#endif + // from string temporary #ifdef CONFIRM_COMPILATION_ERRORS { @@ -794,7 +824,7 @@ SUITE(string_span_tests) TEST(Conversion) { -#ifdef CONFIRM_COMPPILATION_ERRORS +#ifdef CONFIRM_COMPILATION_ERRORS cstring_span<> span = "Hello"; cwstring_span<> wspan{ span }; CHECK(wspan.length() == 5); -- cgit v1.2.3 From 3bc3f35214e456ee801b69a848c5608f5a48823e Mon Sep 17 00:00:00 2001 From: Tristan Brindle Date: Wed, 23 Dec 2015 01:26:38 +1300 Subject: Fix missing #include in string_span.h --- include/string_span.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/string_span.h b/include/string_span.h index c9a4dc9..686ace9 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -23,6 +23,7 @@ #include "gsl_util.h" #include "span.h" #include +#include #ifdef _MSC_VER -- cgit v1.2.3 From d38e6212017077c0629ba2418f9fa894c8ebde96 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 4 Jan 2016 11:57:03 -0800 Subject: Correct missing return in string_span.h Should fix #239. --- include/string_span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_span.h b/include/string_span.h index 686ace9..5143e84 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -435,7 +435,7 @@ public: constexpr const_iterator cend() const noexcept { - span_.cend(); + return span_.cend(); } constexpr reverse_iterator rbegin() const noexcept -- cgit v1.2.3 From fc5fce4f4f8d64fbda523d1b0d55115f5ca68774 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 5 Jan 2016 22:04:00 -0800 Subject: Correct define to undef in string_span.h. --- include/string_span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/string_span.h b/include/string_span.h index 5143e84..ecbee7c 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -902,7 +902,7 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc #undef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG #undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE -#define GSL_MSVC_NO_CPP14_STD_EQUAL +#undef GSL_MSVC_NO_CPP14_STD_EQUAL #undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR #endif // _MSC_VER <= 1800 -- cgit v1.2.3 From 62b063a27258f8d6344a4ddd49ddd8739f71eb49 Mon Sep 17 00:00:00 2001 From: Gary Furnish Date: Wed, 13 Jan 2016 16:12:17 +0000 Subject: Fix issue #242 --- include/span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/span.h b/include/span.h index 72d55d6..31c26ad 100644 --- a/include/span.h +++ b/include/span.h @@ -1818,7 +1818,7 @@ public: auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); size_type size = this->bounds().total_size() / d; - return {(OtherValueType*) this->data(), size, + return {const_cast(reinterpret_cast(this->data())), size, bounds_type{resize_extent(this->bounds().index_bounds(), d), resize_stride(this->bounds().strides(), d)}}; } -- cgit v1.2.3 From b4ff206c24f44e5dbc2dea14d25bfbaf945dbc75 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 3 Feb 2016 19:04:39 -0800 Subject: Added zstring_span and removed zstring_builder to support legacy strings --- include/string_span.h | 65 +++++++++++++++---------- tests/string_span_tests.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 24 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index ecbee7c..3840d55 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -71,17 +71,21 @@ namespace gsl // type system for these types that will not either incur significant runtime costs or // (sometimes needlessly) break existing programs when introduced. // + +template +using basic_zstring = CharT*; + template -using czstring = const char*; +using czstring = basic_zstring; template -using cwzstring = const wchar_t*; +using cwzstring = basic_zstring; template -using zstring = char*; +using zstring = basic_zstring; template -using wzstring = wchar_t*; +using wzstring = basic_zstring; // // ensure_sentinel() @@ -521,43 +525,56 @@ inline std::wstring to_string(wstring_span<> view) #endif +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings template -class basic_zstring_builder +class basic_zstring_span { public: - using impl_type = span; - using string_span_type = basic_string_span; using value_type = CharT; - using pointer = CharT*; - using size_type = typename string_span_type::size_type; - using iterator = typename string_span_type::iterator; + using const_value_type = std::add_const_t; + + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; - basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {} + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; - template - basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {} + using impl_type = span; + using string_span_type = basic_string_span; + + constexpr basic_zstring_span(impl_type span) noexcept + : sp_(span) + { + // expects a zero-terminated span + Expects(span[span.size() - 1] == '\0'); + } - pointer data() const { return sv_.data(); } - string_span_type view() const { return sv_; } + constexpr bool empty() const noexcept { return sp_.size() == 0; } - size_type length() const { return sv_.length(); } + constexpr string_span_type as_string_span() const noexcept { return sp_.first(sp_.size()-1); } - pointer assume0() const { return data(); } - string_span_type ensure_z() const { return gsl::ensure_z(sv_); } + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(sp_); } - iterator begin() const { return sv_.begin(); } - iterator end() const { return sv_.end(); } + constexpr const_zstring_type assume_z() const noexcept { return sp_.data(); } private: - impl_type sv_; + impl_type sp_; }; template -using zstring_builder = basic_zstring_builder; +using zstring_span = basic_zstring_span; template -using wzstring_builder = basic_zstring_builder; -} +using wzstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +} // namespace GSL // operator == template CreateTempName(string_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = 't'; + span[1] = 'm'; + span[2] = 'p'; + last = 3; + } + span[last] = '\0'; + + auto ret = span.subspan(0, 4); + return ret; + } + + TEST(zstring) + { + + // create zspan from zero terminated string + { + char buf[1]; + buf[0] = '\0'; + + zstring_span<> zspan({ buf, 1 }); + + CHECK(strlen(zspan.assume_z()) == 0); + CHECK(zspan.as_string_span().size() == 0); + CHECK(zspan.ensure_z().size() == 0); + } + + // create zspan from non-zero terminated string + { + char buf[1]; + buf[0] = 'a'; + + auto workaround_macro = [&]() { zstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + char buf[10]; + + auto name = CreateTempName({ buf, 10 }); + if (!name.empty()) + { + czstring<> str = name.assume_z(); + CHECK(strlen(str) == 3); + CHECK(*(str+3) == '\0'); + } + } + + } + + cwzstring_span<> CreateTempNameW(wstring_span<> span) + { + Expects(span.size() > 1); + + int last = 0; + if (span.size() > 4) + { + span[0] = L't'; + span[1] = L'm'; + span[2] = L'p'; + last = 3; + } + span[last] = L'\0'; + + auto ret = span.subspan(0, 4); + return ret; + } + + TEST(wzstring) + { + + // create zspan from zero terminated string + { + wchar_t buf[1]; + buf[0] = L'\0'; + + wzstring_span<> zspan({ buf, 1 }); + + CHECK(wcsnlen(zspan.assume_z(), 1) == 0); + CHECK(zspan.as_string_span().size() == 0); + CHECK(zspan.ensure_z().size() == 0); + } + + // create zspan from non-zero terminated string + { + wchar_t buf[1]; + buf[0] = L'a'; + + auto workaround_macro = [&]() { wzstring_span<> zspan({ buf, 1 }); }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + // usage scenario: create zero-terminated temp file name and pass to a legacy API + { + wchar_t buf[10]; + + auto name = CreateTempNameW({ buf, 10 }); + if (!name.empty()) + { + cwzstring<> str = name.assume_z(); + CHECK(wcsnlen(str, 10) == 3); + CHECK(*(str + 3) == L'\0'); + } + } + + } } int main(int, const char *[]) -- cgit v1.2.3 From ed906e7aaebdbd2fccf234b13c9f3e01cf6fde44 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Sat, 6 Feb 2016 10:46:40 -0800 Subject: Addressed CR comments --- include/string_span.h | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 3840d55..786e055 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -527,7 +527,7 @@ inline std::wstring to_string(wstring_span<> view) // zero-terminated string span, used to convert // zero-terminated spans to legacy strings -template +template class basic_zstring_span { public: @@ -544,22 +544,48 @@ public: using string_span_type = basic_string_span; constexpr basic_zstring_span(impl_type span) noexcept - : sp_(span) + : span_(span) { // expects a zero-terminated span Expects(span[span.size() - 1] == '\0'); } - constexpr bool empty() const noexcept { return sp_.size() == 0; } + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; - constexpr string_span_type as_string_span() const noexcept { return sp_.first(sp_.size()-1); } + // move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span(basic_zstring_span&& other) + : span_(std::move(other.span_)) + {} +#endif - constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(sp_); } + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; - constexpr const_zstring_type assume_z() const noexcept { return sp_.data(); } + // move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + constexpr bool empty() const noexcept { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const noexcept { return span_.first(span_.size()-1); } + + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } private: - impl_type sp_; + impl_type span_; }; template -- cgit v1.2.3 From 45f2bdb486ea64afdb7b3045179514291ec1607d Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Sat, 6 Feb 2016 21:37:17 +0000 Subject: Fixed compilation issues with gcc and clang --- include/string_span.h | 8 ++++---- tests/string_span_tests.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 786e055..46bf2d4 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -588,16 +588,16 @@ private: impl_type span_; }; -template +template using zstring_span = basic_zstring_span; -template +template using wzstring_span = basic_zstring_span; -template +template using czstring_span = basic_zstring_span; -template +template using cwzstring_span = basic_zstring_span; } // namespace GSL diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 6abdcb9..28d7353 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -846,7 +846,7 @@ SUITE(string_span_tests) span[last] = '\0'; auto ret = span.subspan(0, 4); - return ret; + return{ ret }; } TEST(zstring) @@ -903,7 +903,7 @@ SUITE(string_span_tests) span[last] = L'\0'; auto ret = span.subspan(0, 4); - return ret; + return{ ret }; } TEST(wzstring) -- cgit v1.2.3 From 6a4f2512b78b4de4003b38f3e67b66c7161ffcee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 8 Feb 2016 12:34:21 +0100 Subject: narrow: Also check if a value has changed sign after cast. Fixes https://github.com/Microsoft/GSL/issues/222. --- include/gsl_util.h | 42 +++++++++++++++++++++--------------------- tests/utils_tests.cpp | 45 +++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index e38868c..3769b8a 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -28,13 +28,13 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr // MSVC 2013 workarounds #if _MSC_VER <= 1800 -// noexcept is not understood +// noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept // turn off some misguided warnings #pragma warning(push) @@ -63,7 +63,7 @@ public: 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; @@ -93,12 +93,12 @@ struct narrowing_error : public std::exception {}; // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template inline T narrow(U u) -{ T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } +{ T t = narrow_cast(u); if (static_cast(t) != u || (t < T{}) != (u < U{})) throw narrowing_error(); return t; } // // at() - Bounds-checked way of accessing static arrays, std::array, std::vector // -template +template constexpr T& at(T(&arr)[N], size_t index) { Expects(index < N); return arr[index]; } @@ -122,7 +122,7 @@ constexpr typename Cont::value_type& at(Cont& cont, size_t index) #undef noexcept #pragma pop_macro("noexcept") - + #pragma warning(pop) #endif // _MSC_VER <= 1800 diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 9406e6b..a46d6e4 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -1,20 +1,20 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include #include #include @@ -95,8 +95,21 @@ SUITE(utils_tests) char c = narrow(n); CHECK(c == 120); - n = 300; + n = 300; CHECK_THROW(narrow(n), narrowing_error); + + const auto int32_max = std::numeric_limits::max(); + const auto int32_min = std::numeric_limits::min(); + + CHECK(narrow(int32_t(0)) == 0); + CHECK(narrow(int32_t(1)) == 1); + CHECK(narrow(int32_max) == int32_max); + + CHECK_THROW(narrow(int32_t(-1)), narrowing_error); + CHECK_THROW(narrow(int32_min), narrowing_error); + + n = -42; + CHECK_THROW(narrow(n), narrowing_error); } } -- cgit v1.2.3 From 1843b1739e30393f9a05693d4c4176eb7fe0aa83 Mon Sep 17 00:00:00 2001 From: galik Date: Tue, 16 Feb 2016 00:57:04 +0000 Subject: Fix: Issue: #267 gsl::fail_fast only defined for one configuration --- include/gsl_assert.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/gsl_assert.h b/include/gsl_assert.h index 81cfd13..51e8ab6 100644 --- a/include/gsl_assert.h +++ b/include/gsl_assert.h @@ -20,6 +20,7 @@ #define GSL_CONTRACTS_H #include +#include // // There are three configuration options for this GSL implementation's behavior @@ -42,10 +43,6 @@ // GSL.assert: assertions // -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#include - namespace gsl { struct fail_fast : public std::runtime_error @@ -54,6 +51,8 @@ struct fail_fast : public std::runtime_error }; } +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + #define Expects(cond) if (!(cond)) \ throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)); #define Ensures(cond) if (!(cond)) \ -- cgit v1.2.3 From 092a8e53e41da8b71996efa32eae40ff35e5a087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 16 Feb 2016 14:29:55 +0100 Subject: narrow: Check for changed sign only if types have different signess. --- include/gsl_util.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index 3769b8a..4ac6187 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -22,6 +22,7 @@ #include "gsl_assert.h" // Ensures/Expects #include #include +#include #include #ifdef _MSC_VER @@ -90,10 +91,24 @@ inline constexpr T narrow_cast(U u) noexcept struct narrowing_error : public std::exception {}; +template +struct is_same_signess +{ + static const bool value = + std::is_signed::value == std::is_signed::value; +}; + // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template inline T narrow(U u) -{ T t = narrow_cast(u); if (static_cast(t) != u || (t < T{}) != (u < U{})) throw narrowing_error(); return t; } +{ + T t = narrow_cast(u); + if (static_cast(t) != u) + throw narrowing_error(); + if (!is_same_signess::value && ((t < T{}) != (u < U{}))) + throw narrowing_error(); + return t; +} // // at() - Bounds-checked way of accessing static arrays, std::array, std::vector -- cgit v1.2.3 From abae0bd998af603d66e929789e054f916cde622b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 19 Feb 2016 19:03:51 +0100 Subject: Refactor is_same_signedness. --- include/gsl_util.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index 4ac6187..b144108 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -91,12 +91,12 @@ inline constexpr T narrow_cast(U u) noexcept struct narrowing_error : public std::exception {}; -template -struct is_same_signess +namespace details { - static const bool value = - std::is_signed::value == std::is_signed::value; -}; + template + struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> + {}; +} // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template @@ -105,7 +105,7 @@ inline T narrow(U u) T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); - if (!is_same_signess::value && ((t < T{}) != (u < U{}))) + if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) throw narrowing_error(); return t; } -- cgit v1.2.3 From c2924406e22d30046e559c93ccd97090d2018522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 19 Feb 2016 19:14:39 +0100 Subject: Disable MSVC warning 4127 (conditional expression is constant) raised for so instances of narrow(). --- include/gsl_util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/gsl_util.h b/include/gsl_util.h index b144108..316f2a1 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -40,6 +40,7 @@ // turn off some misguided warnings #pragma warning(push) #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable: 4127) // conditional expression is constant #endif // _MSC_VER <= 1800 -- cgit v1.2.3 From 49e80625c682fe88baf9e306a784f8be7350e416 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 24 Feb 2016 10:29:29 -0800 Subject: Renamed existing span to multi_span. --- include/span.h | 194 ++++++++++----------- include/string_span.h | 32 ++-- tests/bounds_tests.cpp | 2 +- tests/span_tests.cpp | 390 +++++++++++++++++++++---------------------- tests/strided_span_tests.cpp | 68 ++++---- tests/string_span_tests.cpp | 26 +-- 6 files changed, 356 insertions(+), 356 deletions(-) diff --git a/include/span.h b/include/span.h index 31c26ad..659e116 100644 --- a/include/span.h +++ b/include/span.h @@ -1072,7 +1072,7 @@ struct dim template -class span; +class multi_span; template class strided_span; @@ -1096,7 +1096,7 @@ namespace details template struct SpanArrayTraits { - using type = span; + using type = multi_span; using value_type = T; using bounds_type = static_bounds; using pointer = T*; @@ -1161,7 +1161,7 @@ namespace details }; template - struct is_span_oracle> : std::true_type + struct is_span_oracle> : std::true_type { }; @@ -1177,12 +1177,12 @@ namespace details } template -class span +class multi_span { // TODO do we still need this? template - friend class span; + friend class multi_span; public: using bounds_type = static_bounds; @@ -1193,13 +1193,13 @@ public: using const_value_type = std::add_const_t; using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; - using iterator = contiguous_span_iterator; - using const_span = span; + using iterator = contiguous_span_iterator; + using const_span = multi_span; using const_iterator = contiguous_span_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using sliced_type = - std::conditional_t>; + std::conditional_t>; private: pointer data_; @@ -1210,36 +1210,36 @@ private: public: // default constructor - same as constructing from nullptr_t - constexpr span() noexcept : span(nullptr, bounds_type{}) + constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "Default construction of span only possible " + "Default construction of multi_span only possible " "for dynamic or fixed, zero-length spans."); } - // construct from nullptr - get an empty span - constexpr span(std::nullptr_t) noexcept : span(nullptr, bounds_type{}) + // construct from nullptr - get an empty multi_span + constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of span only possible " + "nullptr_t construction of multi_span only possible " "for dynamic or fixed, zero-length spans."); } // construct from nullptr with size of 0 (helps with template function calls) template ::value>> - constexpr span(std::nullptr_t, IntType size) noexcept : span(nullptr, bounds_type{}) + constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of span only possible " + "nullptr_t construction of multi_span only possible " "for dynamic or fixed, zero-length spans."); Expects(size == 0); } // construct from a single element - constexpr span(reference data) noexcept : span(&data, bounds_type{1}) + constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) { static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || bounds_type::static_size == 1, @@ -1248,13 +1248,13 @@ public: } // prevent constructing from temporaries for single-elements - constexpr span(value_type&&) = delete; + constexpr multi_span(value_type&&) = delete; // construct from pointer + length - constexpr span(pointer ptr, size_type size) noexcept : span(ptr, bounds_type{size}) {} + constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) {} // construct from pointer + length - multidimensional - constexpr span(pointer data, bounds_type bounds) noexcept : data_(data), + constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), bounds_(std::move(bounds)) { Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); @@ -1264,60 +1264,60 @@ public: template ::value && details::LessThan::value>> - constexpr span(pointer begin, Ptr end) - : span(begin, details::newBoundsHelper(static_cast(end) - begin)) + constexpr multi_span(pointer begin, Ptr end) + : multi_span(begin, details::newBoundsHelper(static_cast(end) - begin)) { Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); } // construct from n-dimensions static array template > - constexpr span(T (&arr)[N]) - : span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + constexpr multi_span(T (&arr)[N]) + : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) { static_assert( std::is_convertible::value, - "Cannot convert from source type to target span type."); + "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible::value, - "Cannot construct a span from an array with fewer elements."); + "Cannot construct a multi_span from an array with fewer elements."); } // construct from n-dimensions dynamic array (e.g. new int[m][4]) // (precedence will be lower than the 1-dimension pointer) template > - constexpr span(T* const& data, size_type size) - : span(reinterpret_cast(data), typename Helper::bounds_type{size}) + constexpr multi_span(T* const& data, size_type size) + : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) { static_assert( std::is_convertible::value, - "Cannot convert from source type to target span type."); + "Cannot convert from source type to target multi_span type."); } // construct from std::array template - constexpr span(std::array& arr) : span(arr.data(), bounds_type{static_bounds{}}) + constexpr multi_span(std::array& arr) : multi_span(arr.data(), bounds_type{static_bounds{}}) { static_assert( std::is_convertible(*) []>::value, - "Cannot convert from source type to target span type."); + "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a span from a std::array of smaller size."); + "You cannot construct a multi_span from a std::array of smaller size."); } // construct from const std::array template - constexpr span(const std::array, N>& arr) - : span(arr.data(), static_bounds()) + constexpr multi_span(const std::array, N>& arr) + : multi_span(arr.data(), static_bounds()) { static_assert(std::is_convertible>::value, - "Cannot convert from source type to target span type."); + "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a span from a std::array of smaller size."); + "You cannot construct a multi_span from a std::array of smaller size."); } // prevent constructing from temporary std::array template - constexpr span(std::array&& arr) = delete; + constexpr multi_span(std::array&& arr) = delete; // construct from containers // future: could use contiguous_iterator_traits to identify only contiguous containers @@ -1329,8 +1329,8 @@ public: std::is_same().size(), *std::declval().data())>, DataType>::value>> - constexpr span(Cont& cont) - : span(static_cast(cont.data()), + constexpr multi_span(Cont& cont) + : multi_span(static_cast(cont.data()), details::newBoundsHelper(narrow_cast(cont.size()))) { } @@ -1343,33 +1343,33 @@ public: std::is_same().size(), *std::declval().data())>, DataType>::value>> - explicit constexpr span(Cont&& cont) = delete; + explicit constexpr multi_span(Cont&& cont) = delete; - // construct from a convertible span + // construct from a convertible multi_span template , typename = std::enable_if_t::value && std::is_convertible::value>> - constexpr span(span other) noexcept : data_(other.data_), + constexpr multi_span(multi_span other) noexcept : data_(other.data_), bounds_(other.bounds_) { } // trivial copy and move #ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr span(span&&) = default; + constexpr multi_span(multi_span&&) = default; #endif - constexpr span(const span&) = default; + constexpr multi_span(const multi_span&) = default; // trivial assignment #ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr span& operator=(span&&) = default; + constexpr multi_span& operator=(multi_span&&) = default; #endif - constexpr span& operator=(const span&) = default; + constexpr multi_span& operator=(const multi_span&) = default; - // first() - extract the first Count elements into a new span + // first() - extract the first Count elements into a new multi_span template - constexpr span first() const noexcept + constexpr multi_span first() const noexcept { static_assert(Count >= 0, "Count must be >= 0."); static_assert(bounds_type::static_size == dynamic_range || @@ -1380,16 +1380,16 @@ public: return {this->data(), Count}; } - // first() - extract the first count elements into a new span - constexpr span first(size_type count) const noexcept + // first() - extract the first count elements into a new multi_span + constexpr multi_span first(size_type count) const noexcept { Expects(count >= 0 && count <= this->size()); return {this->data(), count}; } - // last() - extract the last Count elements into a new span + // last() - extract the last Count elements into a new multi_span template - constexpr span last() const noexcept + constexpr multi_span last() const noexcept { static_assert(Count >= 0, "Count must be >= 0."); static_assert(bounds_type::static_size == dynamic_range || @@ -1400,8 +1400,8 @@ public: return {this->data() + this->size() - Count, Count}; } - // last() - extract the last count elements into a new span - constexpr span last(size_type count) const noexcept + // last() - extract the last count elements into a new multi_span + constexpr multi_span last(size_type count) const noexcept { Expects(count >= 0 && count <= this->size()); return {this->data() + this->size() - count, count}; @@ -1409,14 +1409,14 @@ public: // subspan() - create a subview of Count elements starting at Offset template - constexpr span subspan() const noexcept + constexpr multi_span subspan() const noexcept { static_assert(Count >= 0, "Count must be >= 0."); static_assert(Offset >= 0, "Offset must be >= 0."); static_assert(bounds_type::static_size == dynamic_range || ((Offset <= bounds_type::static_size) && Count <= bounds_type::static_size - Offset), - "You must describe a sub-range within bounds of the span."); + "You must describe a sub-range within bounds of the multi_span."); Expects(bounds_type::static_size != dynamic_range || (Offset <= this->size() && Count <= this->size() - Offset)); @@ -1425,7 +1425,7 @@ public: // subspan() - create a subview of count elements starting at offset // supplying dynamic_range for count will consume all available elements from offset - constexpr span subspan(size_type offset, + constexpr multi_span subspan(size_type offset, size_type count = dynamic_range) const noexcept { Expects((offset >= 0 && offset <= this->size()) && @@ -1433,7 +1433,7 @@ public: return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; } - // section - creates a non-contiguous, strided span from a contiguous one + // section - creates a non-contiguous, strided multi_span from a contiguous one constexpr strided_span section(index_type origin, index_type extents) const noexcept { @@ -1442,16 +1442,16 @@ public: strided_bounds{extents, details::make_stride(bounds())}}; } - // length of the span in elements + // length of the multi_span in elements constexpr size_type size() const noexcept { return bounds_.size(); } - // length of the span in elements + // length of the multi_span in elements constexpr size_type length() const noexcept { return this->size(); } - // length of the span in bytes + // length of the multi_span in bytes constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } - // length of the span in bytes + // length of the multi_span in bytes constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } constexpr bool empty() const noexcept { return this->size() == 0; } @@ -1537,7 +1537,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator==(const span& other) const noexcept + constexpr bool operator==(const multi_span& other) const noexcept { return bounds_.size() == other.bounds_.size() && (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); @@ -1546,7 +1546,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator!=(const span& other) const noexcept + constexpr bool operator!=(const multi_span& other) const noexcept { return !(*this == other); } @@ -1554,7 +1554,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator<(const span& other) const noexcept + constexpr bool operator<(const multi_span& other) const noexcept { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } @@ -1562,7 +1562,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator<=(const span& other) const noexcept + constexpr bool operator<=(const multi_span& other) const noexcept { return !(other < *this); } @@ -1570,7 +1570,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator>(const span& other) const noexcept + constexpr bool operator>(const multi_span& other) const noexcept { return (other < *this); } @@ -1578,7 +1578,7 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator>=(const span& other) const noexcept + constexpr bool operator>=(const multi_span& other) const noexcept { return !(*this < other); } @@ -1588,57 +1588,57 @@ public: // Free functions for manipulating spans // -// reshape a span into a different dimensionality +// reshape a multi_span into a different dimensionality // DimCount and Enabled here are workarounds for a bug in MSVC 2015 template 0), typename = std::enable_if_t> -constexpr span as_span(SpanType s, +constexpr multi_span as_span(SpanType s, Dimensions2... dims) { static_assert(details::is_span::value, "Variadic as_span() is for reshaping existing spans."); using BoundsType = - typename span::bounds_type; + typename multi_span::bounds_type; auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); details::verifyBoundsReshape(s.bounds(), tobounds); return {s.data(), tobounds}; } -// convert a span to a span +// convert a multi_span to a multi_span template -span as_bytes(span s) noexcept +multi_span as_bytes(multi_span s) noexcept { static_assert(std::is_trivial>::value, - "The value_type of span must be a trivial type."); + "The value_type of multi_span must be a trivial type."); return {reinterpret_cast(s.data()), s.size_bytes()}; } -// convert a span to a span (a writeable byte span) +// convert a multi_span to a multi_span (a writeable byte multi_span) // this is not currently a portable function that can be relied upon to work // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -span as_writeable_bytes(span s) noexcept +multi_span as_writeable_bytes(multi_span s) noexcept { static_assert(std::is_trivial>::value, - "The value_type of span must be a trivial type."); + "The value_type of multi_span must be a trivial type."); return {reinterpret_cast(s.data()), s.size_bytes()}; } -// convert a span to a span +// convert a multi_span to a multi_span // this is not currently a portable function that can be relied upon to work // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(span s) noexcept - -> span( - span::bounds_type::static_size != dynamic_range +constexpr auto as_span(multi_span s) noexcept + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range ? (static_cast( - span::bounds_type::static_size) / + multi_span::bounds_type::static_size) / sizeof(U)) : dynamic_range)> { - using ConstByteSpan = span; + using ConstByteSpan = multi_span; static_assert( std::is_trivial>::value && (ConstByteSpan::bounds_type::static_size == dynamic_range || @@ -1650,19 +1650,19 @@ constexpr auto as_span(span s) noexcept s.size_bytes() / narrow_cast(sizeof(U))}; } -// convert a span to a span +// convert a multi_span to a multi_span // this is not currently a portable function that can be relied upon to work // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(span s) noexcept -> span< +constexpr auto as_span(multi_span s) noexcept -> multi_span< U, narrow_cast( - span::bounds_type::static_size != dynamic_range - ? static_cast(span::bounds_type::static_size) / + multi_span::bounds_type::static_size != dynamic_range + ? static_cast(multi_span::bounds_type::static_size) / sizeof(U) : dynamic_range)> { - using ByteSpan = span; + using ByteSpan = multi_span; static_assert( std::is_trivial>::value && (ByteSpan::bounds_type::static_size == dynamic_range || @@ -1676,7 +1676,7 @@ constexpr auto as_span(span s) noexcept -> span< template constexpr auto as_span(T* const& ptr, dim... args) - -> span, Dimensions...> + -> multi_span, Dimensions...> { return {reinterpret_cast*>(ptr), details::static_as_span_helper>(args..., details::Sep{})}; @@ -1696,22 +1696,22 @@ constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits:: } template -constexpr span as_span(const std::array& arr) +constexpr multi_span as_span(const std::array& arr) { return {arr}; } template -constexpr span as_span(const std::array&&) = delete; +constexpr multi_span as_span(const std::array&&) = delete; template -constexpr span as_span(std::array& arr) +constexpr multi_span as_span(std::array& arr) { return {arr}; } template -constexpr span as_span(T* begin, T* end) +constexpr multi_span as_span(T* begin, T* end) { return {begin, end}; } @@ -1719,7 +1719,7 @@ constexpr span as_span(T* begin, T* end) template constexpr auto as_span(Cont& arr) -> std::enable_if_t< !details::is_span>::value, - span, dynamic_range>> + multi_span, dynamic_range>> { Expects(arr.size() < PTRDIFF_MAX); return {arr.data(), narrow_cast(arr.size())}; @@ -1728,12 +1728,12 @@ constexpr auto as_span(Cont& arr) -> std::enable_if_t< template constexpr auto as_span(Cont&& arr) -> std::enable_if_t< !details::is_span>::value, - span, dynamic_range>> = delete; + multi_span, dynamic_range>> = delete; // from basic_string which doesn't have nonconst .data() member like other contiguous containers template constexpr auto as_span(std::basic_string& str) - -> span + -> multi_span { Expects(str.size() < PTRDIFF_MAX); return {&str[0], narrow_cast(str.size())}; @@ -1792,7 +1792,7 @@ public: bool Enabled1 = (sizeof...(Dimensions) == Rank), bool Enabled2 = std::is_convertible::value, typename Dummy = std::enable_if_t> - constexpr strided_span(span av, bounds_type bounds) + constexpr strided_span(multi_span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) { } @@ -1988,7 +1988,7 @@ public: private: template - friend class span; + friend class multi_span; pointer data_; const Span* m_validator; diff --git a/include/string_span.h b/include/string_span.h index 46bf2d4..f585d3f 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -96,7 +96,7 @@ using wzstring = basic_zstring; // Will fail-fast if sentinel cannot be found before max elements are examined. // template -span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +multi_span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; @@ -111,34 +111,34 @@ span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) // the limit of size_type. // template -inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +inline multi_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline span ensure_z(char* const& sz, std::ptrdiff_t max) +inline multi_span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +inline multi_span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +inline multi_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +inline multi_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); @@ -146,10 +146,10 @@ inline span ensure_z(const wchar_t* const& sz, std } template -span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } +multi_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template -span::type, dynamic_range> ensure_z(Cont& cont) +multi_span::type, dynamic_range> ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } @@ -228,7 +228,7 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t; using bounds_type = static_bounds; - using impl_type = span; + using impl_type = multi_span; using size_type = ptrdiff_t; using iterator = typename impl_type::iterator; @@ -323,17 +323,17 @@ public: std::is_convertible::value && std::is_convertible, bounds_type>::value> > - constexpr basic_string_span(span other) noexcept + constexpr basic_string_span(multi_span other) noexcept : span_(other) {} #else // from span - constexpr basic_string_span(span other) noexcept + constexpr basic_string_span(multi_span other) noexcept : span_(other) {} template , value_type>::value>> - constexpr basic_string_span(span, Extent> other) noexcept + constexpr basic_string_span(multi_span, Extent> other) noexcept : span_(other) {} #endif @@ -540,14 +540,14 @@ public: using zstring_type = basic_zstring; using const_zstring_type = basic_zstring; - using impl_type = span; + using impl_type = multi_span; using string_span_type = basic_string_span; - constexpr basic_zstring_span(impl_type span) noexcept - : span_(span) + constexpr basic_zstring_span(impl_type multi_span) noexcept + : span_(multi_span) { // expects a zero-terminated span - Expects(span[span.size() - 1] == '\0'); + Expects(multi_span[multi_span.size() - 1] == '\0'); } // copy diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index 0665260..ab8c5bd 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -58,7 +58,7 @@ SUITE(bounds_test) auto itr = bounds.begin(); (void)itr; #ifdef CONFIRM_COMPILATION_ERRORS - span av(nullptr, bounds); + multi_span av(nullptr, bounds); auto itr2 = av.cbegin(); diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8b39639..0d1170d 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -43,33 +43,33 @@ SUITE(span_tests) TEST(default_constructor) { { - span s; + multi_span s; CHECK(s.length() == 0 && s.data() == nullptr); - span cs; + multi_span cs; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - span s; + multi_span s; CHECK(s.length() == 0 && s.data() == nullptr); - span cs; + multi_span cs; CHECK(cs.length() == 0 && cs.data() == nullptr); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s; + multi_span s; CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif } { - span s{}; + multi_span s{}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{}; + multi_span cs{}; CHECK(cs.length() == 0 && cs.data() == nullptr); } } @@ -77,41 +77,41 @@ SUITE(span_tests) TEST(from_nullptr_constructor) { { - span s = nullptr; + multi_span s = nullptr; CHECK(s.length() == 0 && s.data() == nullptr); - span cs = nullptr; + multi_span cs = nullptr; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - span s = nullptr; + multi_span s = nullptr; CHECK(s.length() == 0 && s.data() == nullptr); - span cs = nullptr; + multi_span cs = nullptr; CHECK(cs.length() == 0 && cs.data() == nullptr); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s = nullptr; + multi_span s = nullptr; CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif } { - span s{nullptr}; + multi_span s{nullptr}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr}; + multi_span cs{nullptr}; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - span s{nullptr}; + multi_span s{nullptr}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr}; + multi_span cs{nullptr}; CHECK(cs.length() == 0 && cs.data() == nullptr); } } @@ -119,49 +119,49 @@ SUITE(span_tests) TEST(from_nullptr_length_constructor) { { - span s{nullptr, 0}; + multi_span s{nullptr, 0}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + multi_span cs{nullptr, 0}; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - span s{nullptr, 0}; + multi_span s{nullptr, 0}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + multi_span cs{nullptr, 0}; CHECK(cs.length() == 0 && cs.data() == nullptr); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{nullptr, 0}; + multi_span s{nullptr, 0}; CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile #endif } { - auto workaround_macro = []() { span s{nullptr, 1}; }; + auto workaround_macro = []() { multi_span s{nullptr, 1}; }; CHECK_THROW(workaround_macro(), fail_fast); - auto const_workaround_macro = []() { span cs{nullptr, 1}; }; + auto const_workaround_macro = []() { multi_span cs{nullptr, 1}; }; CHECK_THROW(const_workaround_macro(), fail_fast); } { - auto workaround_macro = []() { span s{nullptr, 1}; }; + auto workaround_macro = []() { multi_span s{nullptr, 1}; }; CHECK_THROW(workaround_macro(), fail_fast); - auto const_workaround_macro = []() { span s{nullptr, 1}; }; + auto const_workaround_macro = []() { multi_span s{nullptr, 1}; }; CHECK_THROW(const_workaround_macro(), fail_fast); } { - span s{nullptr, 0}; + multi_span s{nullptr, 0}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + multi_span cs{nullptr, 0}; CHECK(cs.length() == 0 && cs.data() == nullptr); } } @@ -171,11 +171,11 @@ SUITE(span_tests) int i = 5; { - span s = i; + multi_span s = i; CHECK(s.length() == 1 && s.data() == &i); CHECK(s[0] == 5); - span cs = i; + multi_span cs = i; CHECK(cs.length() == 1 && cs.data() == &i); CHECK(cs[0] == 5); } @@ -183,26 +183,26 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS const j = 1; - span s = j; + multi_span s = j; #endif } { #ifdef CONFIRM_COMPILATION_ERRORS - span s = i; + multi_span s = i; CHECK(s.length() == 0 && s.data() == &i); #endif } { - span s = i; + multi_span s = i; CHECK(s.length() == 1 && s.data() == &i); CHECK(s[0] == 5); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s = i; + multi_span s = i; CHECK(s.length() == 2 && s.data() == &i); #endif } @@ -210,7 +210,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_a_temp = []() -> int { return 4; }; - auto use_a_span = [](span s) { (void) s; }; + auto use_a_span = [](multi_span s) { (void) s; }; use_a_span(get_a_temp()); #endif } @@ -221,26 +221,26 @@ SUITE(span_tests) int arr[4] = {1, 2, 3, 4}; { - span s{&arr[0], 2}; + multi_span s{&arr[0], 2}; CHECK(s.length() == 2 && s.data() == &arr[0]); CHECK(s[0] == 1 && s[1] == 2); } { - span s{&arr[0], 2}; + multi_span s{&arr[0], 2}; CHECK(s.length() == 2 && s.data() == &arr[0]); CHECK(s[0] == 1 && s[1] == 2); } { int* p = nullptr; - span s{p, 0}; + multi_span s{p, 0}; CHECK(s.length() == 0 && s.data() == nullptr); } { int* p = nullptr; - auto workaround_macro = [=]() { span s{p, 2}; }; + auto workaround_macro = [=]() { multi_span s{p, 2}; }; CHECK_THROW(workaround_macro(), fail_fast); } } @@ -250,47 +250,47 @@ SUITE(span_tests) int arr[4] = {1, 2, 3, 4}; { - span s{&arr[0], &arr[2]}; + multi_span s{&arr[0], &arr[2]}; CHECK(s.length() == 2 && s.data() == &arr[0]); CHECK(s[0] == 1 && s[1] == 2); } { - span s{&arr[0], &arr[2]}; + multi_span s{&arr[0], &arr[2]}; CHECK(s.length() == 2 && s.data() == &arr[0]); CHECK(s[0] == 1 && s[1] == 2); } { - span s{&arr[0], &arr[0]}; + multi_span s{&arr[0], &arr[0]}; CHECK(s.length() == 0 && s.data() == &arr[0]); } { - span s{&arr[0], &arr[0]}; + multi_span s{&arr[0], &arr[0]}; CHECK(s.length() == 0 && s.data() == &arr[0]); } { - auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + auto workaround_macro = [&]() { multi_span s{&arr[1], &arr[0]}; }; CHECK_THROW(workaround_macro(), fail_fast); } { int* p = nullptr; - auto workaround_macro = [&]() { span s{&arr[0], p}; }; + auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; CHECK_THROW(workaround_macro(), fail_fast); } { int* p = nullptr; - auto workaround_macro = [&]() { span s{p, p}; }; + auto workaround_macro = [&]() { multi_span s{p, p}; }; CHECK_THROW(workaround_macro(), fail_fast); } { int* p = nullptr; - auto workaround_macro = [&]() { span s{&arr[0], p}; }; + auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; CHECK_THROW(workaround_macro(), fail_fast); } } @@ -300,64 +300,64 @@ SUITE(span_tests) int arr[5] = {1, 2, 3, 4, 5}; { - span s{arr}; + multi_span s{arr}; CHECK(s.length() == 5 && s.data() == &arr[0]); } { - span s{arr}; + multi_span s{arr}; CHECK(s.length() == 5 && s.data() == &arr[0]); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr}; + multi_span s{arr}; #endif } { - span s{arr}; + multi_span s{arr}; CHECK(s.length() == 0 && s.data() == &arr[0]); } int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; { - span s{arr2d}; + multi_span s{arr2d}; CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); CHECK(s[0] == 1 && s[5] == 6); } { - span s{arr2d}; + multi_span s{arr2d}; CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; + multi_span s{arr2d}; #endif } { - span s{arr2d}; + multi_span s{arr2d}; CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); CHECK(s[0] == 1 && s[5] == 6); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; + multi_span s{arr2d}; #endif } { - span s{arr2d[0]}; + multi_span s{arr2d[0]}; CHECK(s.length() == 1 && s.data() == &arr2d[0]); } { - span s{arr2d}; + multi_span s{arr2d}; CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; CHECK(workaround_macro()); @@ -365,48 +365,48 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; + multi_span s{arr2d}; #endif } int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; { - span s{arr3d}; + multi_span s{arr3d}; CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); CHECK(s[0] == 1 && s[11] == 12); } { - span s{arr3d}; + multi_span s{arr3d}; CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; + multi_span s{arr3d}; #endif } { - span s{arr3d}; + multi_span s{arr3d}; CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); CHECK(s[0] == 1 && s[5] == 6); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; + multi_span s{arr3d}; #endif } { - span s{arr3d[0]}; + multi_span s{arr3d[0]}; CHECK(s.length() == 1 && s.data() == &arr3d[0]); } { - span s{arr3d}; + multi_span s{arr3d}; CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; CHECK(workaround_macro()); @@ -414,7 +414,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; + multi_span s{arr3d}; #endif } } @@ -424,23 +424,23 @@ SUITE(span_tests) double(*arr)[3][4] = new double[100][3][4]; { - span s(arr, 10); + multi_span s(arr, 10); CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); CHECK_THROW(s[10][3][4], fail_fast); } { - span s(arr, 10); + multi_span s(arr, 10); CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); } { - span s(arr, 10); + multi_span s(arr, 10); CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); } { - span s(arr, 0); + multi_span s(arr, 0); CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); } @@ -452,54 +452,54 @@ SUITE(span_tests) std::array arr = {1, 2, 3, 4}; { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - span cs{arr}; + multi_span cs{arr}; CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - span cs{arr}; + multi_span cs{arr}; CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == 2 && s.data() == arr.data()); - span cs{arr}; + multi_span cs{arr}; CHECK(cs.size() == 2 && cs.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == 0 && s.data() == arr.data()); - span cs{arr}; + multi_span cs{arr}; CHECK(cs.size() == 0 && cs.data() == arr.data()); } // TODO This is currently an unsupported scenario. We will come back to it as we revise // the multidimensional interface and what transformations between dimensionality look like //{ - // span s{arr}; + // multi_span s{arr}; // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); //} { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr}; + multi_span s{arr}; #endif } { #ifdef CONFIRM_COMPILATION_ERRORS auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; - auto take_a_span = [](span s) { (void) s; }; + auto take_a_span = [](multi_span s) { (void) s; }; // try to take a temporary std::array take_a_span(get_an_array()); #endif @@ -511,42 +511,42 @@ SUITE(span_tests) const std::array arr = {1, 2, 3, 4}; { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == 2 && s.data() == arr.data()); } { - span s{arr}; + multi_span s{arr}; CHECK(s.size() == 0 && s.data() == arr.data()); } // TODO This is currently an unsupported scenario. We will come back to it as we revise // the multidimensional interface and what transformations between dimensionality look like //{ - // span s{arr}; + // multi_span s{arr}; // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); //} { #ifdef CONFIRM_COMPILATION_ERRORS - span s{arr}; + multi_span s{arr}; #endif } { #ifdef CONFIRM_COMPILATION_ERRORS auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span s) { (void) s; }; + auto take_a_span = [](multi_span s) { (void) s; }; // try to take a temporary std::array take_a_span(get_an_array()); #endif @@ -559,10 +559,10 @@ SUITE(span_tests) const std::vector cv = v; { - span s{v}; + multi_span s{v}; CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); - span cs{v}; + multi_span cs{v}; CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); } @@ -571,18 +571,18 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS - span s{str}; + multi_span s{str}; CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); #endif - span cs{str}; + multi_span cs{str}; CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); } { #ifdef CONFIRM_COMPILATION_ERRORS - span s{cstr}; + multi_span s{cstr}; #endif - span cs{cstr}; + multi_span cs{cstr}; CHECK(cs.size() == narrow_cast(cstr.size()) && cs.data() == cstr.data()); } @@ -590,7 +590,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](multi_span s) { (void) s; }; use_span(get_temp_vector()); #endif } @@ -598,7 +598,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](multi_span s) { (void) s; }; use_span(get_temp_string()); #endif } @@ -606,7 +606,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_vector = []() -> const std::vector { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](multi_span s) { (void) s; }; use_span(get_temp_vector()); #endif } @@ -614,7 +614,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_string = []() -> const std::string { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](multi_span s) { (void) s; }; use_span(get_temp_string()); #endif } @@ -622,7 +622,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS std::map m; - span s{m}; + multi_span s{m}; #endif } } @@ -630,9 +630,9 @@ SUITE(span_tests) TEST(from_convertible_span_constructor) { #ifdef CONFIRM_COMPILATION_ERRORS - span av1(nullptr, b1); + multi_span av1(nullptr, b1); - auto f = [&]() { span av1(nullptr); }; + auto f = [&]() { multi_span av1(nullptr); }; CHECK_THROW(f(), fail_fast); #endif @@ -641,34 +641,34 @@ SUITE(span_tests) b12 = b11; b11 = b12; - span av1 = nullptr; - span av2(av1); - span av2(av1); + multi_span av1 = nullptr; + multi_span av2(av1); + multi_span av2(av1); #endif - span avd; + multi_span avd; #ifdef CONFIRM_COMPILATION_ERRORS - span avb = avd; + multi_span avb = avd; #endif - span avcd = avd; + multi_span avcd = avd; (void) avcd; } TEST(copy_move_and_assignment) { - span s1; + multi_span s1; CHECK(s1.empty()); int arr[] = {3, 4, 5}; - span s2 = arr; + multi_span s2 = arr; CHECK(s2.length() == 3 && s2.data() == &arr[0]); s2 = s1; CHECK(s2.empty()); - auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; - auto use_span = [&](span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; + auto get_temp_span = [&]() -> multi_span { return {&arr[1], 2}; }; + auto use_span = [&](multi_span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; use_span(get_temp_span()); s1 = get_temp_span(); @@ -708,28 +708,28 @@ SUITE(span_tests) int arr[5] = {1, 2, 3, 4, 5}; { - span av = arr; + multi_span av = arr; CHECK((av.first<2>().bounds() == static_bounds<2>())); CHECK(av.first<2>().length() == 2); CHECK(av.first(2).length() == 2); } { - span av = arr; + multi_span av = arr; CHECK((av.first<0>().bounds() == static_bounds<0>())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } { - span av = arr; + multi_span av = arr; CHECK((av.first<5>().bounds() == static_bounds<5>())); CHECK(av.first<5>().length() == 5); CHECK(av.first(5).length() == 5); } { - span av = arr; + multi_span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK(av.first<6>().bounds() == static_bounds<6>()); CHECK(av.first<6>().length() == 6); @@ -739,7 +739,7 @@ SUITE(span_tests) } { - span av; + multi_span av; CHECK((av.first<0>().bounds() == static_bounds<0>())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); @@ -751,28 +751,28 @@ SUITE(span_tests) int arr[5] = {1, 2, 3, 4, 5}; { - span av = arr; + multi_span av = arr; CHECK((av.last<2>().bounds() == static_bounds<2>())); CHECK(av.last<2>().length() == 2); CHECK(av.last(2).length() == 2); } { - span av = arr; + multi_span av = arr; CHECK((av.last<0>().bounds() == static_bounds<0>())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } { - span av = arr; + multi_span av = arr; CHECK((av.last<5>().bounds() == static_bounds<5>())); CHECK(av.last<5>().length() == 5); CHECK(av.last(5).length() == 5); } { - span av = arr; + multi_span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK((av.last<6>().bounds() == static_bounds<6>())); CHECK(av.last<6>().length() == 6); @@ -781,7 +781,7 @@ SUITE(span_tests) } { - span av; + multi_span av; CHECK((av.last<0>().bounds() == static_bounds<0>())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); @@ -793,7 +793,7 @@ SUITE(span_tests) int arr[5] = {1, 2, 3, 4, 5}; { - span av = arr; + multi_span av = arr; CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); CHECK((av.subspan<2, 2>().length() == 2)); CHECK(av.subspan(2, 2).length() == 2); @@ -801,14 +801,14 @@ SUITE(span_tests) } { - span av = arr; + multi_span av = arr; CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); CHECK((av.subspan<0, 0>().length() == 0)); CHECK(av.subspan(0, 0).length() == 0); } { - span av = arr; + multi_span av = arr; CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); CHECK((av.subspan<0, 5>().length() == 5)); CHECK(av.subspan(0, 5).length() == 5); @@ -817,7 +817,7 @@ SUITE(span_tests) } { - span av = arr; + multi_span av = arr; CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); CHECK((av.subspan<5, 0>().length() == 0)); CHECK(av.subspan(5, 0).length() == 0); @@ -825,7 +825,7 @@ SUITE(span_tests) } { - span av; + multi_span av; CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); CHECK((av.subspan<0, 0>().length() == 0)); CHECK(av.subspan(0, 0).length() == 0); @@ -833,13 +833,13 @@ SUITE(span_tests) } { - span av; + multi_span av; CHECK(av.subspan(0).length() == 0); CHECK_THROW(av.subspan(1).length(), fail_fast); } { - span av = arr; + multi_span av = arr; CHECK(av.subspan(0).length() == 5); CHECK(av.subspan(1).length() == 4); CHECK(av.subspan(4).length() == 1); @@ -850,7 +850,7 @@ SUITE(span_tests) } { - span av = arr; + multi_span av = arr; CHECK(av.subspan(0).length() == 5); CHECK(av.subspan(1).length() == 4); CHECK(av.subspan(4).length() == 1); @@ -866,18 +866,18 @@ SUITE(span_tests) int arr[2] = {1, 2}; { - span s; + multi_span s; CHECK(s.rank() == 1); } { - span s = arr; + multi_span s = arr; CHECK(s.rank() == 1); } int arr2d[1][1] = {}; { - span s = arr2d; + multi_span s = arr2d; CHECK(s.rank() == 2); } } @@ -885,7 +885,7 @@ SUITE(span_tests) TEST(extent) { { - span s; + multi_span s; CHECK(s.extent() == 0); CHECK(s.extent(0) == 0); CHECK_THROW(s.extent(1), fail_fast); @@ -895,7 +895,7 @@ SUITE(span_tests) } { - span s; + multi_span s; CHECK(s.extent() == 0); CHECK(s.extent(0) == 0); CHECK_THROW(s.extent(1), fail_fast); @@ -904,7 +904,7 @@ SUITE(span_tests) { int arr2d[1][2] = {}; - span s = arr2d; + multi_span s = arr2d; CHECK(s.extent() == 1); CHECK(s.extent<0>() == 1); CHECK(s.extent<1>() == 2); @@ -916,7 +916,7 @@ SUITE(span_tests) { int arr2d[1][2] = {}; - span s = arr2d; + multi_span s = arr2d; CHECK(s.extent() == 0); CHECK(s.extent<0>() == 0); CHECK(s.extent<1>() == 2); @@ -931,7 +931,7 @@ SUITE(span_tests) int arr[4] = {1, 2, 3, 4}; { - span s = arr; + multi_span s = arr; CHECK(s(0) == 1); CHECK_THROW(s(5), fail_fast); } @@ -939,7 +939,7 @@ SUITE(span_tests) int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; { - span s = arr2d; + multi_span s = arr2d; CHECK(s(0, 0) == 1); CHECK(s(1, 2) == 6); } @@ -950,11 +950,11 @@ SUITE(span_tests) { int arr[10][2]; auto s1 = as_span(arr); - span s2 = s1; + multi_span s2 = s1; CHECK(s1 == s2); - span s3 = as_span(s1, dim<>(20)); + multi_span s3 = as_span(s1, dim<>(20)); CHECK(s3 == s2 && s3 == s1); } @@ -978,8 +978,8 @@ SUITE(span_tests) { int arr[] = {2, 1}; // bigger - span s1 = nullptr; - span s2 = arr; + multi_span s1 = nullptr; + multi_span s2 = arr; CHECK(s1 != s2); CHECK(s2 != s1); @@ -998,8 +998,8 @@ SUITE(span_tests) { int arr1[] = {1, 2}; int arr2[] = {1, 2}; - span s1 = arr1; - span s2 = arr2; + multi_span s1 = arr1; + multi_span s2 = arr2; CHECK(s1 == s2); CHECK(!(s1 != s2)); @@ -1018,8 +1018,8 @@ SUITE(span_tests) { int arr[] = {1, 2, 3}; - span s1 = {&arr[0], 2}; // shorter - span s2 = arr; // longer + multi_span s1 = {&arr[0], 2}; // shorter + multi_span s2 = arr; // longer CHECK(s1 != s2); CHECK(s2 != s1); @@ -1039,8 +1039,8 @@ SUITE(span_tests) int arr1[] = {1, 2}; // smaller int arr2[] = {2, 1}; // bigger - span s1 = arr1; - span s2 = arr2; + multi_span s1 = arr1; + multi_span s2 = arr2; CHECK(s1 != s2); CHECK(s2 != s1); @@ -1086,21 +1086,21 @@ SUITE(span_tests) CHECK_THROW((av[{10, 2}]), fail_fast); } - void overloaded_func(span exp, int expected_value) + void overloaded_func(multi_span exp, int expected_value) { for (auto val : exp) { CHECK(val == expected_value); } } - void overloaded_func(span exp, char expected_value) + void overloaded_func(multi_span exp, char expected_value) { for (auto val : exp) { CHECK(val == expected_value); } } - void fixed_func(span exp, int expected_value) + void fixed_func(multi_span exp, int expected_value) { for (auto val : exp) { CHECK(val == expected_value); @@ -1185,10 +1185,10 @@ SUITE(span_tests) { string str; - span strspan = as_span(str); + multi_span strspan = as_span(str); (void) strspan; const string cstr; - span cstrspan = as_span(cstr); + multi_span cstrspan = as_span(cstr); (void) cstrspan; } @@ -1218,7 +1218,7 @@ SUITE(span_tests) TEST(empty_spans) { { - span empty_av(nullptr); + multi_span empty_av(nullptr); CHECK(empty_av.bounds().index_bounds() == index<1>{0}); CHECK_THROW(empty_av[0], fail_fast); @@ -1231,7 +1231,7 @@ SUITE(span_tests) } { - span empty_av = {}; + multi_span empty_av = {}; CHECK(empty_av.bounds().index_bounds() == index<1>{0}); CHECK_THROW(empty_av[0], fail_fast); CHECK_THROW(empty_av.begin()[0], fail_fast); @@ -1251,7 +1251,7 @@ SUITE(span_tests) arr[2 * i + 1] = i; } - span av(arr, 8); + multi_span av(arr, 8); ptrdiff_t a[1] = {0}; index<1> i = a; @@ -1395,7 +1395,7 @@ SUITE(span_tests) } } - void iterate_second_column(span av) + void iterate_second_column(multi_span av) { auto length = av.size() / 2; @@ -1456,22 +1456,22 @@ SUITE(span_tests) // static bounds { - span av = arr; + multi_span av = arr; iterate_second_column(av); } // first bound is dynamic { - span av = arr; + multi_span av = arr; iterate_second_column(av); } // second bound is dynamic { - span av = arr; + multi_span av = arr; iterate_second_column(av); } // both bounds are dynamic { - span av = arr; + multi_span av = arr; iterate_second_column(av); } } @@ -1490,17 +1490,17 @@ SUITE(span_tests) // first bound is dynamic { - span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } // second bound is dynamic { - span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } // both bounds are dynamic { - span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } @@ -1510,7 +1510,7 @@ SUITE(span_tests) TEST(span_structure_size) { double(*arr)[3][4] = new double[100][3][4]; - span av1(arr, 10); + multi_span av1(arr, 10); struct EffectiveStructure { @@ -1521,7 +1521,7 @@ SUITE(span_tests) CHECK_THROW(av1[10][3][4], fail_fast); - span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + multi_span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); (void) av2; } @@ -1529,46 +1529,46 @@ SUITE(span_tests) { int arr[] = {1, 2, 3, 4}; - // converting to an span from an equal size array is ok - span av4 = arr; + // converting to an multi_span from an equal size array is ok + multi_span av4 = arr; CHECK(av4.length() == 4); // converting to dynamic_range a_v is always ok { - span av = av4; + multi_span av = av4; (void) av; } { - span av = arr; + multi_span av = arr; (void) av; } -// initialization or assignment to static span that REDUCES size is NOT ok +// initialization or assignment to static multi_span that REDUCES size is NOT ok #ifdef CONFIRM_COMPILATION_ERRORS { - span av2 = arr; + multi_span av2 = arr; } { - span av2 = av4; + multi_span av2 = av4; } #endif { - span av = arr; - span av2 = av; + multi_span av = arr; + multi_span av2 = av; (void) av2; } #ifdef CONFIRM_COMPILATION_ERRORS { - span av = arr; - span av2 = av.as_span(dim<2>(), dim<2>()); + multi_span av = arr; + multi_span av2 = av.as_span(dim<2>(), dim<2>()); } #endif { - span av = arr; - span av2 = as_span(av, dim<>(2), dim<>(2)); + multi_span av = arr; + multi_span av2 = as_span(av, dim<>(2), dim<>(2)); auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; CHECK(workaround_macro()); } @@ -1577,45 +1577,45 @@ SUITE(span_tests) // you can convert statically { - span av2 = {arr, 2}; + multi_span av2 = {arr, 2}; (void) av2; } { - span av2 = av4.first<1>(); + multi_span av2 = av4.first<1>(); (void) av2; } // ...or dynamically { - // NB: implicit conversion to span from span - span av2 = av4.first(1); + // NB: implicit conversion to multi_span from multi_span + multi_span av2 = av4.first(1); (void) av2; } - // initialization or assignment to static span that requires size INCREASE is not ok. + // initialization or assignment to static multi_span that requires size INCREASE is not ok. int arr2[2] = {1, 2}; #ifdef CONFIRM_COMPILATION_ERRORS { - span av4 = arr2; + multi_span av4 = arr2; } { - span av2 = arr2; - span av4 = av2; + multi_span av2 = arr2; + multi_span av4 = av2; } #endif { auto f = [&]() { - span av9 = {arr2, 2}; + multi_span av9 = {arr2, 2}; (void) av9; }; CHECK_THROW(f(), fail_fast); } // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one - span av = arr2; + multi_span av = arr2; auto f = [&]() { - span av2 = av; + multi_span av2 = av; (void) av2; }; CHECK_THROW(f(), fail_fast); @@ -1628,13 +1628,13 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS // you should not be able to get writeable bytes for const objects - span av = a; + multi_span av = a; auto wav = av.as_writeable_bytes(); #endif } { - span av; + multi_span av; auto wav = as_writeable_bytes(av); CHECK(wav.length() == av.length()); CHECK(wav.length() == 0); @@ -1642,7 +1642,7 @@ SUITE(span_tests) } { - span av = a; + multi_span av = a; auto wav = as_writeable_bytes(av); CHECK(wav.data() == (byte*) &a[0]); CHECK(wav.length() == sizeof(a)); @@ -1654,7 +1654,7 @@ SUITE(span_tests) int a[] = {1, 2, 3, 4}; { - span av = a; + multi_span av = a; auto wav = as_writeable_bytes(av); for (auto& b : wav) { b = byte(0); @@ -1665,7 +1665,7 @@ SUITE(span_tests) } { - span av = a; + multi_span av = a; for (auto& n : av) { n = 1; } diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index 0fbf1d7..8b96ddf 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -49,7 +49,7 @@ SUITE(strided_span_tests) { std::vector data(5 * 10); std::iota(begin(data), end(data), 0); - const span av = as_span(span{data}, dim<5>(), dim<10>()); + const multi_span av = as_span(multi_span{data}, dim<5>(), dim<10>()); strided_span av_section_1 = av.section({ 1, 2 }, { 3, 4 }); CHECK((av_section_1[{0, 0}] == 12)); @@ -87,13 +87,13 @@ SUITE(strided_span_tests) CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7)); } - // Check span constructor + // Check multi_span constructor { int arr[] = { 1, 2 }; // From non-cv-qualified source { - const span src = arr; + const multi_span src = arr; strided_span sav{ src, {2, 1} }; CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); @@ -102,9 +102,9 @@ SUITE(strided_span_tests) #if _MSC_VER > 1800 //strided_span sav_c{ {src}, {2, 1} }; - strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_c{ multi_span{src}, strided_bounds<1>{2, 1} }; #else - strided_span sav_c{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_c{ multi_span{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_c.bounds().strides() == index<1>{ 1 }); @@ -113,7 +113,7 @@ SUITE(strided_span_tests) #if _MSC_VER > 1800 strided_span sav_v{ src, {2, 1} }; #else - strided_span sav_v{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_v{ multi_span{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_v.bounds().strides() == index<1>{ 1 }); @@ -122,7 +122,7 @@ SUITE(strided_span_tests) #if _MSC_VER > 1800 strided_span sav_cv{ src, {2, 1} }; #else - strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_cv{ multi_span{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); @@ -131,7 +131,7 @@ SUITE(strided_span_tests) // From const-qualified source { - const span src{ arr }; + const multi_span src{ arr }; strided_span sav_c{ src, {2, 1} }; CHECK(sav_c.bounds().index_bounds() == index<1>{ 2 }); @@ -141,7 +141,7 @@ SUITE(strided_span_tests) #if _MSC_VER > 1800 strided_span sav_cv{ src, {2, 1} }; #else - strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_cv{ multi_span{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); @@ -151,7 +151,7 @@ SUITE(strided_span_tests) // From volatile-qualified source { - const span src{ arr }; + const multi_span src{ arr }; strided_span sav_v{ src, {2, 1} }; CHECK(sav_v.bounds().index_bounds() == index<1>{ 2 }); @@ -161,7 +161,7 @@ SUITE(strided_span_tests) #if _MSC_VER > 1800 strided_span sav_cv{ src, {2, 1} }; #else - strided_span sav_cv{ span{src}, strided_bounds<1>{2, 1} }; + strided_span sav_cv{ multi_span{src}, strided_bounds<1>{2, 1} }; #endif CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); CHECK(sav_cv.bounds().strides() == index<1>{ 1 }); @@ -170,7 +170,7 @@ SUITE(strided_span_tests) // From cv-qualified source { - const span src{ arr }; + const multi_span src{ arr }; strided_span sav_cv{ src, {2, 1} }; CHECK(sav_cv.bounds().index_bounds() == index<1>{ 2 }); @@ -183,11 +183,11 @@ SUITE(strided_span_tests) { int arr[2] = { 4, 5 }; - const span av(arr, 2); - span av2{ av }; + const multi_span av(arr, 2); + multi_span av2{ av }; CHECK(av2[1] == 5); - static_assert(std::is_convertible, span>::value, "ctor is not implicit!"); + static_assert(std::is_convertible, multi_span>::value, "ctor is not implicit!"); const strided_span src{ arr, {2, 1} }; strided_span sav{ src }; @@ -258,13 +258,13 @@ SUITE(strided_span_tests) { std::vector data(5 * 10); std::iota(begin(data), end(data), 0); - const span src = as_span(span{data}, dim<5>(), dim<10>()); + const multi_span src = as_span(multi_span{data}, dim<5>(), dim<10>()); const strided_span sav{ src, {{5, 10}, {10, 1}} }; #ifdef CONFIRM_COMPILATION_ERRORS const strided_span csav{ {src},{ { 5, 10 },{ 10, 1 } } }; #endif - const strided_span csav{ span{ src }, { { 5, 10 },{ 10, 1 } } }; + const strided_span csav{ multi_span{ src }, { { 5, 10 },{ 10, 1 } } }; strided_span sav_sl = sav[2]; CHECK(sav_sl[0] == 20); @@ -317,7 +317,7 @@ SUITE(strided_span_tests) TEST(strided_span_bounds) { int arr[] = { 0, 1, 2, 3 }; - span av(arr); + multi_span av(arr); { // incorrect sections @@ -432,7 +432,7 @@ SUITE(strided_span_tests) TEST(strided_span_type_conversion) { int arr[] = { 0, 1, 2, 3 }; - span av(arr); + multi_span av(arr); { strided_span sav{ av.data(), av.size(), { av.size() / 2, 2 } }; @@ -447,7 +447,7 @@ SUITE(strided_span_tests) #endif } - span bytes = as_bytes(av); + multi_span bytes = as_bytes(av); // retype strided array with regular strides - from raw data { @@ -460,10 +460,10 @@ SUITE(strided_span_tests) CHECK_THROW(sav3[0][1], fail_fast); } - // retype strided array with regular strides - from span + // retype strided array with regular strides - from multi_span { strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; strided_span sav3 = sav2.as_strided_span(); CHECK(sav3[0][0] == 0); @@ -475,7 +475,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - last dimension of the array is too small { strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } }; - span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -483,7 +483,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides are too small { strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; - span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -491,7 +491,7 @@ SUITE(strided_span_tests) // 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 bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -499,7 +499,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides does not divide by the new typesize { strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } }; - span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -511,7 +511,7 @@ SUITE(strided_span_tests) CHECK_THROW(sav2.as_strided_span(), fail_fast); } - // retype strided array with irregular strides - from span + // retype strided array with irregular strides - from multi_span { strided_bounds<1> bounds{ bytes.size() / 2, 2 }; strided_span sav2{ bytes, bounds }; @@ -522,7 +522,7 @@ SUITE(strided_span_tests) TEST(empty_strided_spans) { { - span empty_av(nullptr); + multi_span empty_av(nullptr); strided_span empty_sav{ empty_av, { 0, 1 } }; CHECK(empty_sav.bounds().index_bounds() == index<1>{ 0 }); @@ -553,7 +553,7 @@ SUITE(strided_span_tests) } } - void iterate_every_other_element(span av) + void iterate_every_other_element(multi_span av) { // pick every other element @@ -586,13 +586,13 @@ SUITE(strided_span_tests) // static bounds { - span av(arr, 8); + multi_span av(arr, 8); iterate_every_other_element(av); } // dynamic bounds { - span av(arr, 8); + multi_span av(arr, 8); iterate_every_other_element(av); } } @@ -612,7 +612,7 @@ SUITE(strided_span_tests) delete[] arr; } - void iterate_second_slice(span av) + void iterate_second_slice(multi_span av) { int expected[6] = {2,3,10,11,18,19}; auto section = av.section({0,1,0}, {3,1,2}); @@ -653,7 +653,7 @@ SUITE(strided_span_tests) } { - span av = arr; + multi_span av = arr; iterate_second_slice(av); } } @@ -693,7 +693,7 @@ SUITE(strided_span_tests) TEST(strided_span_conversion) { - // get an span of 'c' values from the list of X's + // get an multi_span of 'c' values from the list of X's struct X { int a; int b; int c; }; diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 28d7353..cd301a0 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -142,7 +142,7 @@ SUITE(string_span_tests) const char* ptr = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::span sp = ensure_z("Hello"); + gsl::multi_span sp = ensure_z("Hello"); // comparison to literal CHECK(span == cstring_span<>("Hello")); @@ -182,7 +182,7 @@ SUITE(string_span_tests) char* ptr = ar; std::string str = "Hello"; std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::span sp = ensure_z(ar1); + gsl::multi_span sp = ensure_z(ar1); // comparison to static array with no null termination CHECK(span == string_span<>(ar)); @@ -216,7 +216,7 @@ SUITE(string_span_tests) const char ar2[10] = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::span sp = ensure_z("Hello"); + gsl::multi_span sp = ensure_z("Hello"); cstring_span<> span = "Hello"; @@ -253,7 +253,7 @@ SUITE(string_span_tests) char* _ptr = _ar; std::string _str = "Hello"; std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::span _sp{ _ar, 5 }; + gsl::multi_span _sp{ _ar, 5 }; CHECK(span == _ar); CHECK(span == _ar1); @@ -447,7 +447,7 @@ SUITE(string_span_tests) // from span of a final extent { - span sp = "Hello"; + multi_span sp = "Hello"; cstring_span<> span = sp; CHECK(span.length() == 6); } @@ -455,7 +455,7 @@ SUITE(string_span_tests) // from const span of a final extent to non-const string_span #ifdef CONFIRM_COMPILATION_ERRORS { - span sp = "Hello"; + multi_span sp = "Hello"; string_span<> span = sp; CHECK(span.length() == 6); } @@ -568,7 +568,7 @@ SUITE(string_span_tests) // from const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const span inner = vec; + const multi_span inner = vec; cstring_span<> span = inner; CHECK(span.length() == 5); } @@ -576,7 +576,7 @@ SUITE(string_span_tests) // from non-const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - span inner = vec; + multi_span inner = vec; cstring_span<> span = inner; CHECK(span.length() == 5); } @@ -675,7 +675,7 @@ SUITE(string_span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const span inner = vec; + const multi_span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -684,7 +684,7 @@ SUITE(string_span_tests) // from non-const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - span inner = vec; + multi_span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); } @@ -693,7 +693,7 @@ SUITE(string_span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const span inner = vec; + const multi_span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -769,12 +769,12 @@ SUITE(string_span_tests) // move span { - span span = ensure_z("Hello"); + multi_span span = ensure_z("Hello"); cstring_span<> span1 = std::move(span); CHECK(span1.length() == 5); } { - span span = ensure_z("Hello"); + multi_span span = ensure_z("Hello"); cstring_span<> span2 = move_wrapper(std::move(span)); CHECK(span2.length() == 5); } -- cgit v1.2.3 From d2f12a8fa3df7d0debaa683c4c4cabcaeea383a1 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 24 Feb 2016 11:03:33 -0800 Subject: File renames to reflect new multi_span name. --- include/gsl.h | 3 +- include/multi_span.h | 2224 ++++++++++++++++++++++++++++++++++++++++++ include/span.h | 2224 ------------------------------------------ include/string_span.h | 2 +- tests/CMakeLists.txt | 4 +- tests/bounds_tests.cpp | 2 +- tests/multi_span_tests.cpp | 1679 +++++++++++++++++++++++++++++++ tests/span_tests.cpp | 1679 ------------------------------- tests/strided_span_tests.cpp | 2 +- 9 files changed, 3910 insertions(+), 3909 deletions(-) create mode 100644 include/multi_span.h delete mode 100644 include/span.h create mode 100644 tests/multi_span_tests.cpp delete mode 100644 tests/span_tests.cpp diff --git a/include/gsl.h b/include/gsl.h index ad064ba..03f8545 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -21,7 +21,8 @@ #include "gsl_assert.h" // Ensures/Expects #include "gsl_util.h" // finally()/narrow()/narrow_cast()... -#include "span.h" // span, strided_span... +//#include "span.h" // span +#include "multi_span.h" // multi_span, strided_span... #include "string_span.h" // zstring, string_span, zstring_builder... #include diff --git a/include/multi_span.h b/include/multi_span.h new file mode 100644 index 0000000..659e116 --- /dev/null +++ b/include/multi_span.h @@ -0,0 +1,2224 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include "gsl_assert.h" +#include "gsl_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + template + class are_integral : public std::integral_constant + { + }; + + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; +} + +template +class index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class index; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr index() noexcept {} + + constexpr index(const value_type (&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template < + typename T, typename... Ts, + typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) + : index({narrow_cast(t), narrow_cast(ds)...}) + { + } +#else + template ::value>> + constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + { + } +#endif + + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } + + constexpr index operator+() const noexcept { return *this; } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept + { + return rhs * v; + } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return narrow_cast(-1); + } + + template ::value>> + constexpr bool operator==(T other) const noexcept + { + return narrow_cast(-1) == other; + } + + template ::value>> + constexpr bool operator!=(T other) const noexcept + { + return narrow_cast(-1) != other; + } +}; + +template ::value>> +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges + { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + { + } + + BoundsRanges(const BoundsRanges&) = default; + BoundsRanges& operator=(const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) {} + BoundsRanges() = default; + + template + void serialize(T&) const + { + } + + template + size_type linearize(const T&) const + { + return 0; + } + + template + size_type contains(const T&) const + { + return -1; + } + + size_type elementNum(size_t) const noexcept { return 0; } + + size_type totalSize() const noexcept { return TotalSize; } + + bool operator==(const BoundsRanges&) const noexcept { return true; } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + Expects(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + Expects(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept { return m_bound; } + + size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + Expects(arr[Dim] < CurrentRange); // Index is out of range + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } + + size_type elementNum() const noexcept { return CurrentRange; } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; + + template + struct TypeListIndexer + { + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) + { + return obj_; + } + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) + { + return TypeListIndexer(static_cast(obj_)).template get(); + } + + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain& obj) + { + return TypeListIndexer(obj); + } + + template 1), + typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) {} +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + + template + friend class static_bounds; + +public: + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(static_cast(il.begin())) + { + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new (&m_ranges) MyRanges(otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast&>(m_ranges)}; + } + + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return {extents}; + } + + template + constexpr bool operator==(const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator!=(const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds&) noexcept = default; + + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; + + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + { + } + + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), + m_strides(strides) + { + } + + constexpr index_type strides() const noexcept { return m_strides; } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const noexcept + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) { + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const noexcept { return m_strides[0]; } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return {details::shift_left(m_extents), details::shift_left(m_strides)}; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const noexcept { return m_extents; } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; + +template +class bounds_iterator : public std::iterator +{ +private: + using Base = std::iterator; + +public: + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept { return curr_; } + + constexpr pointer operator->() const noexcept { return &curr_; } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) { + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; + return *this; + } + curr_[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr_ = boundary_; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr_, boundary_)) { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { + curr_[i] = boundary_[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) { + if (curr_[i] >= 1) { + curr_[i]--; + return *this; + } + curr_[i] = boundary_[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + Expects(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr_) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) { + stride[i] = stride[i + 1] * boundary_[i + 1]; + } + for (size_t i = 0; i < rank; ++i) { + curr_[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + // index is out of bounds of the array + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr_) - linearize(rhs.curr_); + } + + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr_ == rhs.curr_; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr_, rhs.curr_); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); + } + +private: + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary_)) { + res = 1; + for (size_t i = rank; i-- > 0;) { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary_[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) { + res += idx[i] * multiplier; + multiplier *= boundary_[i]; + } + } + return res; + } + + value_type boundary_; + std::remove_const_t curr_; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) noexcept +{ + return rhs + n; +} + +namespace details +{ + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return {stride}; + } + + template + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + static_assert(is_bounds::value && is_bounds::value, + "The src type and dest type must be bounds"); + static_assert(std::is_same::value, + "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || + BoundsSrc::static_size == dynamic_range || + BoundsDest::static_size == BoundsSrc::static_size, + "The source bounds must have same size as dest bounds"); + Expects(src.size() == dest.size()); + } + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; +enum class byte : std::uint8_t +{ +}; + +template +struct dim +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim(std::ptrdiff_t size) : dvalue(size) {} +}; + +template +class multi_span; + +template +class strided_span; + +namespace details +{ + template + struct SpanTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct SpanTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct SpanArrayTraits + { + using type = multi_span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct SpanArrayTraits : SpanArrayTraits + { + }; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + Expects(BoundsType::static_size <= totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); + } + + struct Sep + { + }; + + template + T static_as_span_helper(Sep, Args... args) + { + return T{narrow_cast(args)...}; + } + template + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_span_helper(Arg, Args... args) + { + return static_as_span_helper(args...); + } + template + T static_as_span_helper(dim val, Args... args) + { + return static_as_span_helper(args..., val.dvalue); + } + + template + struct static_as_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : is_span_oracle> + { + }; +} + +template +class multi_span +{ + // TODO do we still need this? + template + friend class multi_span; + +public: + using bounds_type = static_bounds; + static const size_t Rank = bounds_type::rank; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = contiguous_span_iterator; + using const_span = multi_span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + +public: + // default constructor - same as constructing from nullptr_t + constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr - get an empty multi_span + constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + Expects(size == 0); + } + + // construct from a single element + constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } + + // prevent constructing from temporaries for single-elements + constexpr multi_span(value_type&&) = delete; + + // construct from pointer + length + constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) {} + + // construct from pointer + length - multidimensional + constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + } + + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr multi_span(pointer begin, Ptr end) + : multi_span(begin, details::newBoundsHelper(static_cast(end) - begin)) + { + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + } + + // construct from n-dimensions static array + template > + constexpr multi_span(T (&arr)[N]) + : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + { + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot construct a multi_span from an array with fewer elements."); + } + + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr multi_span(T* const& data, size_type size) + : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) + { + static_assert( + std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + } + + // construct from std::array + template + constexpr multi_span(std::array& arr) : multi_span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert( + std::is_convertible(*) []>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // construct from const std::array + template + constexpr multi_span(const std::array, N>& arr) + : multi_span(arr.data(), static_bounds()) + { + static_assert(std::is_convertible>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // prevent constructing from temporary std::array + template + constexpr multi_span(std::array&& arr) = delete; + + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr multi_span(Cont& cont) + : multi_span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) + { + } + + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr multi_span(Cont&& cont) = delete; + + // construct from a convertible multi_span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr multi_span(multi_span other) noexcept : data_(other.data_), + bounds_(other.bounds_) + { + } + +// trivial copy and move +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span(multi_span&&) = default; +#endif + constexpr multi_span(const multi_span&) = default; + +// trivial assignment +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span& operator=(multi_span&&) = default; +#endif + constexpr multi_span& operator=(const multi_span&) = default; + + // first() - extract the first Count elements into a new multi_span + template + constexpr multi_span first() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; + } + + // first() - extract the first count elements into a new multi_span + constexpr multi_span first(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; + } + + // last() - extract the last Count elements into a new multi_span + template + constexpr multi_span last() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; + } + + // last() - extract the last count elements into a new multi_span + constexpr multi_span last(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; + } + + // subspan() - create a subview of Count elements starting at Offset + template + constexpr multi_span subspan() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the multi_span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; + } + + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr multi_span subspan(size_type offset, + size_type count = dynamic_range) const noexcept + { + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; + } + + // section - creates a non-contiguous, strided multi_span from a contiguous one + constexpr strided_span section(index_type origin, index_type extents) const + noexcept + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; + } + + // length of the multi_span in elements + constexpr size_type size() const noexcept { return bounds_.size(); } + + // length of the multi_span in elements + constexpr size_type length() const noexcept { return this->size(); } + + // length of the multi_span in bytes + constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } + + // length of the multi_span in bytes + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } + + constexpr bool empty() const noexcept { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + return bounds_.extent(dim); + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + constexpr pointer data() const noexcept { return data_; } + + template + constexpr reference operator()(FirstIndex index) + { + return this->operator[](narrow_cast(index)); + } + + template + constexpr reference operator()(FirstIndex index, OtherIndices... indices) + { + index_type idx = {narrow_cast(index), + narrow_cast(indices...)}; + return this->operator[](idx); + } + + constexpr reference operator[](const index_type& idx) const noexcept + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const noexcept + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; + } + + constexpr iterator begin() const noexcept { return iterator{this, true}; } + + constexpr iterator end() const noexcept { return iterator{this, false}; } + + constexpr const_iterator cbegin() const noexcept + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const noexcept + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator{cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const multi_span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const multi_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const multi_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const multi_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const multi_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const multi_span& other) const noexcept + { + return !(*this < other); + } +}; + +// +// Free functions for manipulating spans +// + +// reshape a multi_span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr multi_span as_span(SpanType s, + Dimensions2... dims) +{ + static_assert(details::is_span::value, + "Variadic as_span() is for reshaping existing spans."); + using BoundsType = + typename multi_span::bounds_type; + auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a multi_span to a multi_span +template +multi_span as_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span (a writeable byte multi_span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +multi_span as_writeable_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(multi_span s) noexcept + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range + ? (static_cast( + multi_span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_span(multi_span s) noexcept -> multi_span< + U, narrow_cast( + multi_span::bounds_type::static_size != dynamic_range + ? static_cast(multi_span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +template +constexpr auto as_span(T* const& ptr, dim... args) + -> multi_span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), + details::static_as_span_helper>(args..., details::Sep{})}; +} + +template +constexpr auto as_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +{ + return {arr}; +} + +template +constexpr multi_span as_span(const std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_span(const std::array&&) = delete; + +template +constexpr multi_span as_span(std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_span(T* begin, T* end) +{ + return {begin, end}; +} + +template +constexpr auto as_span(Cont& arr) -> std::enable_if_t< + !details::is_span>::value, + multi_span, dynamic_range>> +{ + Expects(arr.size() < PTRDIFF_MAX); + return {arr.data(), narrow_cast(arr.size())}; +} + +template +constexpr auto as_span(Cont&& arr) -> std::enable_if_t< + !details::is_span>::value, + multi_span, dynamic_range>> = delete; + +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_span(std::basic_string& str) + -> multi_span +{ + Expects(str.size() < PTRDIFF_MAX); + return {&str[0], narrow_cast(str.size())}; +} + +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. +template +class strided_span +{ +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + template + friend class strided_span; + +public: + // from raw data + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : data_(ptr), bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void) size; + } + + // from static array of size N + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } + + // from array view + template ::value, + typename Dummy = std::enable_if_t> + constexpr strided_span(multi_span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + + // convertible + template ::value>> + constexpr strided_span(const strided_span& other) + : data_(other.data_), bounds_(other.bounds_) + { + } + + // convert from bytes + template + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return {const_cast(reinterpret_cast(this->data())), size, + bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; + } + + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); + } + + constexpr size_type size() const noexcept { return bounds_.size(); } + + constexpr pointer data() const noexcept { return data_; } + + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } + + constexpr iterator begin() const { return iterator{this, true}; } + + constexpr iterator end() const { return iterator{this, false}; } + + constexpr const_iterator cbegin() const + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } + + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const noexcept + { + return !(*this < other); + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + // The last dimension of the array needs to contain a multiple of new type elements + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); + + index_type ret = extent; + ret[Rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + + for (size_t i = Rank - 1; i > 0; --i) { + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + } + + index_type ret = strides / d; + ret[Rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + +private: + template + friend class multi_span; + + pointer data_; + const Span* m_validator; + void validateThis() const + { + // iterator is out of range of the array + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + } + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + +public: + reference operator*() const noexcept + { + validateThis(); + return *data_; + } + pointer operator->() const noexcept + { + validateThis(); + return data_; + } + contiguous_span_iterator& operator++() noexcept + { + ++data_; + return *this; + } + contiguous_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() noexcept + { + --data_; + return *this; + } + contiguous_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) noexcept + { + data_ += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ - rhs.data_; + } + reference operator[](difference_type n) const noexcept { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ == rhs.data_; + } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ < rhs.data_; + } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(contiguous_span_iterator& rhs) noexcept + { + std::swap(data_, rhs.data_); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +class general_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + +private: + template + friend class strided_span; + + const Span* m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } + +public: + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + difference_type operator-(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept + { + return (*m_container)[m_itr[n]]; + ; + } + bool operator==(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(general_span_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +} // namespace gsl + +#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_VARIADIC_CTOR_BUG + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#endif // GSL_SPAN_H diff --git a/include/span.h b/include/span.h deleted file mode 100644 index 659e116..0000000 --- a/include/span.h +++ /dev/null @@ -1,2224 +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_SPAN_H -#define GSL_SPAN_H - -#include "gsl_assert.h" -#include "gsl_util.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - -// noexcept is not understood -#ifndef GSL_THROW_ON_CONTRACT_VIOLATION -#pragma push_macro("noexcept") -#define noexcept /* nothing */ -#endif - -// turn off some misguided warnings -#pragma warning(push) -#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable : 4512) // warns that assignment op could not be generated - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#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 -{ - -/* -** begin definitions of index and bounds -*/ -namespace details -{ - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; - - template - class are_integral : public std::integral_constant - { - }; - - template - class are_integral - : public std::integral_constant::value && are_integral::value> - { - }; -} - -template -class index final -{ - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class index; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr index() noexcept {} - - constexpr index(const value_type (&values)[Rank]) noexcept - { - std::copy(values, values + Rank, elems); - } - -#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template < - typename T, typename... Ts, - typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) - : index({narrow_cast(t), narrow_cast(ds)...}) - { - } -#else - template ::value>> - constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} - { - } -#endif - - constexpr index(const index& other) noexcept = default; - - constexpr index& operator=(const index& rhs) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const noexcept - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - constexpr bool operator==(const index& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } - - constexpr index operator+() const noexcept { return *this; } - - constexpr index operator-() const noexcept - { - index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr index operator+(const index& rhs) const noexcept - { - index ret = *this; - ret += rhs; - return ret; - } - - constexpr index operator-(const index& rhs) const noexcept - { - index ret = *this; - ret -= rhs; - return ret; - } - - constexpr index& operator+=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr index operator*(value_type v) const noexcept - { - index ret = *this; - ret *= v; - return ret; - } - - constexpr index operator/(value_type v) const noexcept - { - index ret = *this; - ret /= v; - return ret; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return rhs * v; - } - - constexpr index& operator*=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } - -private: - value_type elems[Rank] = {}; -}; - -#ifndef _MSC_VER - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const noexcept - { - return narrow_cast(-1); - } - - template ::value>> - constexpr bool operator==(T other) const noexcept - { - return narrow_cast(-1) == other; - } - - template ::value>> - constexpr bool operator!=(T other) const noexcept - { - return narrow_cast(-1) != other; - } -}; - -template ::value>> -constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right == left; -} - -template ::value>> -constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const std::ptrdiff_t dynamic_range = -1; -#endif - -struct generalized_mapping_tag -{ -}; -struct contiguous_mapping_tag : generalized_mapping_tag -{ -}; - -namespace details -{ - - template - struct LessThan - { - static const bool value = Left < Right; - }; - - template - struct BoundsRanges - { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; - - // TODO : following signature is for work around VS bug - template - BoundsRanges(const OtherRange&, bool /* firstLevel */) - { - } - - BoundsRanges(const BoundsRanges&) = default; - BoundsRanges& operator=(const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) {} - BoundsRanges() = default; - - template - void serialize(T&) const - { - } - - template - size_type linearize(const T&) const - { - return 0; - } - - template - size_type contains(const T&) const - { - return -1; - } - - size_type elementNum(size_t) const noexcept { return 0; } - - size_type totalSize() const noexcept { return TotalSize; } - - bool operator==(const BoundsRanges&) const noexcept { return true; } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) - : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - Expects(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, - bool /* firstLevel */ = true) - : Base(static_cast&>(other), false) - , m_bound(other.totalSize()) - { - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - const size_type index = this->Base::totalSize() * arr[Dim]; - Expects(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const noexcept { return m_bound; } - - size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return m_bound == rhs.m_bound && - static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = - Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges& other, - bool firstLevel = true) - : Base(static_cast&>(other), false) - { - (void) firstLevel; - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - Expects(arr[Dim] < CurrentRange); // Index is out of range - return this->Base::totalSize() * arr[Dim] + - this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - if (arr[Dim] >= CurrentRange) return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } - - size_type elementNum() const noexcept { return CurrentRange; } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible - : public std::integral_constant= TargetType::TotalSize || - TargetType::TotalSize == dynamic_range || - SourceType::TotalSize == dynamic_range || - TargetType::TotalSize == 0)> - { - }; - - template - struct TypeListIndexer - { - const TypeChain& obj_; - TypeListIndexer(const TypeChain& obj) : obj_(obj) {} - - template - const TypeChain& getObj(std::true_type) - { - return obj_; - } - - template - auto getObj(std::false_type) - -> decltype(TypeListIndexer(static_cast(obj_)).template get()) - { - return TypeListIndexer(static_cast(obj_)).template get(); - } - - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain& obj) - { - return TypeListIndexer(obj); - } - - template 1), - typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) { - ret[i] = other[i + 1]; - } - return ret; - } -} - -template -class bounds_iterator; - -template -class static_bounds -{ -public: - static_bounds(const details::BoundsRanges&) {} -}; - -template -class static_bounds -{ - using MyRanges = details::BoundsRanges; - - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} - - template - friend class static_bounds; - -public: - static const size_t rank = MyRanges::Depth; - static const size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; - - using size_type = std::ptrdiff_t; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; - - constexpr static_bounds(const static_bounds&) = default; - - template - struct BoundsRangeConvertible2; - - template > - static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant())) - { - }; - - template - struct BoundsRangeConvertible2 : std::true_type - { - }; - - template - struct BoundsRangeConvertible - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant::value || - TargetType::CurrentRange == dynamic_range || - SourceType::CurrentRange == dynamic_range)>())) - { - }; - - template - struct BoundsRangeConvertible : std::true_type - { - }; - - template , - details::BoundsRanges>::value>> - constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - { - Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || - MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); - } - - constexpr static_bounds(std::initializer_list il) - : m_ranges(static_cast(il.begin())) - { - // Size of the initializer list must match the rank of the array - Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || - MyRanges::DynamicNum == il.size()); - // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); - } - - constexpr static_bounds() = default; - - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new (&m_ranges) MyRanges(otherBounds.m_ranges); - return *this; - } - - constexpr sliced_type slice() const noexcept - { - return sliced_type{static_cast&>(m_ranges)}; - } - - constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } - - constexpr size_type size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - - constexpr bool contains(const index_type& idx) const noexcept - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](size_t index) const noexcept - { - return m_ranges.elementNum(index); - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, - "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - static_assert(std::is_integral::value, - "Dimension parameter must be supplied as an integral type."); - auto real_dim = narrow_cast(dim); - Expects(real_dim < rank); - - return m_ranges.elementNum(real_dim); - } - - constexpr index_type index_bounds() const noexcept - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return {extents}; - } - - template - constexpr bool operator==(const static_bounds& rhs) const noexcept - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator!=(const static_bounds& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } - - constexpr const_iterator end() const noexcept - { - return const_iterator(*this, this->index_bounds()); - } -}; - -template -class strided_bounds -{ - template - friend class strided_bounds; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; - - constexpr strided_bounds(const strided_bounds&) noexcept = default; - - constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; - - constexpr strided_bounds(const value_type (&values)[rank], index_type strides) - : m_extents(values), m_strides(std::move(strides)) - { - } - - constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept - : m_extents(extents), - m_strides(strides) - { - } - - constexpr index_type strides() const noexcept { return m_strides; } - - constexpr size_type total_size() const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; ++i) { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - - constexpr size_type size() const noexcept - { - size_type ret = 1; - for (size_t i = 0; i < rank; ++i) { - ret *= m_extents[i]; - } - return ret; - } - - constexpr bool contains(const index_type& idx) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; i++) { - Expects(idx[i] < m_extents[i]); // index is out of bounds of the array - ret += idx[i] * m_strides[i]; - } - return ret; - } - - constexpr size_type stride() const noexcept { return m_strides[0]; } - - template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return {details::shift_left(m_extents), details::shift_left(m_strides)}; - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - - constexpr index_type index_bounds() const noexcept { return m_extents; } - constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } - - constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } - -private: - index_type m_extents; - index_type m_strides; -}; - -template -struct is_bounds : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; - -template -class bounds_iterator : public std::iterator -{ -private: - using Base = std::iterator; - -public: - static const size_t rank = IndexType::rank; - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const noexcept { return curr_; } - - constexpr pointer operator->() const noexcept { return &curr_; } - - constexpr bounds_iterator& operator++() noexcept - { - for (size_t i = rank; i-- > 0;) { - if (curr_[i] < boundary_[i] - 1) { - curr_[i]++; - return *this; - } - curr_[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr_ = boundary_; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - if (!less(curr_, boundary_)) { - // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) { - curr_[i] = boundary_[i] - 1; - } - return *this; - } - for (size_t i = rank; i-- > 0;) { - if (curr_[i] >= 1) { - curr_[i]--; - return *this; - } - curr_[i] = boundary_[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - Expects(false); - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - auto linear_idx = linearize(curr_) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary_[i + 1]; - } - for (size_t i = 0; i < rank; ++i) { - curr_[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - // index is out of bounds of the array - Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept { return * this += -n; } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return linearize(curr_) - linearize(rhs.curr_); - } - - constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr_ == rhs.curr_; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return less(curr_, rhs.curr_); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - - void swap(bounds_iterator& rhs) noexcept - { - std::swap(boundary_, rhs.boundary_); - std::swap(curr_, rhs.curr_); - } - -private: - constexpr bool less(index_type& one, index_type& other) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (one[i] < other[i]) return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const noexcept - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary_)) { - res = 1; - for (size_t i = rank; i-- > 0;) { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary_[i]; - } - } - else - { - for (size_t i = rank; i-- > 0;) { - res += idx[i] * multiplier; - multiplier *= boundary_[i]; - } - } - return res; - } - - value_type boundary_; - std::remove_const_t curr_; -}; - -template -bounds_iterator operator+(typename bounds_iterator::difference_type n, - const bounds_iterator& rhs) noexcept -{ - return rhs + n; -} - -namespace details -{ - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return {stride}; - } - - template - void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) - { - static_assert(is_bounds::value && is_bounds::value, - "The src type and dest type must be bounds"); - static_assert(std::is_same::value, - "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || - BoundsSrc::static_size == dynamic_range || - BoundsDest::static_size == BoundsSrc::static_size, - "The source bounds must have same size as dest bounds"); - Expects(src.size() == dest.size()); - } - -} // namespace details - -template -class contiguous_span_iterator; -template -class general_span_iterator; -enum class byte : std::uint8_t -{ -}; - -template -struct dim -{ - static const std::ptrdiff_t value = DimSize; -}; -template <> -struct dim -{ - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - dim(std::ptrdiff_t size) : dvalue(size) {} -}; - -template -class multi_span; - -template -class strided_span; - -namespace details -{ - template - struct SpanTypeTraits - { - using value_type = T; - using size_type = size_t; - }; - - template - struct SpanTypeTraits::type> - { - using value_type = typename Traits::span_traits::value_type; - using size_type = typename Traits::span_traits::size_type; - }; - - template - struct SpanArrayTraits - { - using type = multi_span; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct SpanArrayTraits : SpanArrayTraits - { - }; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - Expects(BoundsType::static_size <= totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl( - totalSize, std::integral_constant()); - } - - struct Sep - { - }; - - template - T static_as_span_helper(Sep, Args... args) - { - return T{narrow_cast(args)...}; - } - template - std::enable_if_t< - !std::is_same>::value && !std::is_same::value, T> - static_as_span_helper(Arg, Args... args) - { - return static_as_span_helper(args...); - } - template - T static_as_span_helper(dim val, Args... args) - { - return static_as_span_helper(args..., val.dvalue); - } - - template - struct static_as_span_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_span_oracle : std::false_type - { - }; - - template - struct is_span_oracle> : std::true_type - { - }; - - template - struct is_span_oracle> : std::true_type - { - }; - - template - struct is_span : is_span_oracle> - { - }; -} - -template -class multi_span -{ - // TODO do we still need this? - template - friend class multi_span; - -public: - using bounds_type = static_bounds; - static const size_t Rank = bounds_type::rank; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = contiguous_span_iterator; - using const_span = multi_span; - using const_iterator = contiguous_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - -public: - // default constructor - same as constructing from nullptr_t - constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "Default construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr - get an empty multi_span - constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr with size of 0 (helps with template function calls) - template ::value>> - constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - Expects(size == 0); - } - - // construct from a single element - constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) - { - static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || - bounds_type::static_size == 1, - "Construction from a single element only possible " - "for dynamic or fixed spans of length 0 or 1."); - } - - // prevent constructing from temporaries for single-elements - constexpr multi_span(value_type&&) = delete; - - // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) {} - - // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), - bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); - } - - // construct from begin,end pointer pair - template ::value && - details::LessThan::value>> - constexpr multi_span(pointer begin, Ptr end) - : multi_span(begin, details::newBoundsHelper(static_cast(end) - begin)) - { - Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); - } - - // construct from n-dimensions static array - template > - constexpr multi_span(T (&arr)[N]) - : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) - { - static_assert( - std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible::value, - "Cannot construct a multi_span from an array with fewer elements."); - } - - // construct from n-dimensions dynamic array (e.g. new int[m][4]) - // (precedence will be lower than the 1-dimension pointer) - template > - constexpr multi_span(T* const& data, size_type size) - : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) - { - static_assert( - std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - } - - // construct from std::array - template - constexpr multi_span(std::array& arr) : multi_span(arr.data(), bounds_type{static_bounds{}}) - { - static_assert( - std::is_convertible(*) []>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // construct from const std::array - template - constexpr multi_span(const std::array, N>& arr) - : multi_span(arr.data(), static_bounds()) - { - static_assert(std::is_convertible>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // prevent constructing from temporary std::array - template - constexpr multi_span(std::array&& arr) = delete; - - // construct from containers - // future: could use contiguous_iterator_traits to identify only contiguous containers - // type-requirements: container must have .size(), operator[] which are value_type compatible - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - constexpr multi_span(Cont& cont) - : multi_span(static_cast(cont.data()), - details::newBoundsHelper(narrow_cast(cont.size()))) - { - } - - // prevent constructing from temporary containers - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - explicit constexpr multi_span(Cont&& cont) = delete; - - // construct from a convertible multi_span - template , - typename = std::enable_if_t::value && - std::is_convertible::value>> - constexpr multi_span(multi_span other) noexcept : data_(other.data_), - bounds_(other.bounds_) - { - } - -// trivial copy and move -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span(multi_span&&) = default; -#endif - constexpr multi_span(const multi_span&) = default; - -// trivial assignment -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span& operator=(multi_span&&) = default; -#endif - constexpr multi_span& operator=(const multi_span&) = default; - - // first() - extract the first Count elements into a new multi_span - template - constexpr multi_span first() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data(), Count}; - } - - // first() - extract the first count elements into a new multi_span - constexpr multi_span first(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data(), count}; - } - - // last() - extract the last Count elements into a new multi_span - template - constexpr multi_span last() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data() + this->size() - Count, Count}; - } - - // last() - extract the last count elements into a new multi_span - constexpr multi_span last(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data() + this->size() - count, count}; - } - - // subspan() - create a subview of Count elements starting at Offset - template - constexpr multi_span subspan() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(Offset >= 0, "Offset must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - ((Offset <= bounds_type::static_size) && - Count <= bounds_type::static_size - Offset), - "You must describe a sub-range within bounds of the multi_span."); - - Expects(bounds_type::static_size != dynamic_range || - (Offset <= this->size() && Count <= this->size() - Offset)); - return {this->data() + Offset, Count}; - } - - // subspan() - create a subview of count elements starting at offset - // supplying dynamic_range for count will consume all available elements from offset - constexpr multi_span subspan(size_type offset, - size_type count = dynamic_range) const noexcept - { - Expects((offset >= 0 && offset <= this->size()) && - (count == dynamic_range || (count <= this->size() - offset))); - return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; - } - - // section - creates a non-contiguous, strided multi_span from a contiguous one - constexpr strided_span section(index_type origin, index_type extents) const - noexcept - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - strided_bounds{extents, details::make_stride(bounds())}}; - } - - // length of the multi_span in elements - constexpr size_type size() const noexcept { return bounds_.size(); } - - // length of the multi_span in elements - constexpr size_type length() const noexcept { return this->size(); } - - // length of the multi_span in bytes - constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } - - // length of the multi_span in bytes - constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } - - constexpr bool empty() const noexcept { return this->size() == 0; } - - static constexpr std::size_t rank() { return Rank; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "Dimension should be less than rank (dimension count starts from 0)."); - return bounds_.template extent(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - return bounds_.extent(dim); - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - constexpr pointer data() const noexcept { return data_; } - - template - constexpr reference operator()(FirstIndex index) - { - return this->operator[](narrow_cast(index)); - } - - template - constexpr reference operator()(FirstIndex index, OtherIndices... indices) - { - index_type idx = {narrow_cast(index), - narrow_cast(indices...)}; - return this->operator[](idx); - } - - constexpr reference operator[](const index_type& idx) const noexcept - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const noexcept - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return Ret{data_ + ridx, bounds_.slice()}; - } - - constexpr iterator begin() const noexcept { return iterator{this, true}; } - - constexpr iterator end() const noexcept { return iterator{this, false}; } - - constexpr const_iterator cbegin() const noexcept - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const noexcept - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const noexcept - { - return const_reverse_iterator{cend()}; - } - - constexpr const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator{cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const multi_span& other) const noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const multi_span& other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const multi_span& other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const multi_span& other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const multi_span& other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const multi_span& other) const noexcept - { - return !(*this < other); - } -}; - -// -// Free functions for manipulating spans -// - -// reshape a multi_span into a different dimensionality -// DimCount and Enabled here are workarounds for a bug in MSVC 2015 -template 0), typename = std::enable_if_t> -constexpr multi_span as_span(SpanType s, - Dimensions2... dims) -{ - static_assert(details::is_span::value, - "Variadic as_span() is for reshaping existing spans."); - using BoundsType = - typename multi_span::bounds_type; - auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(s.bounds(), tobounds); - return {s.data(), tobounds}; -} - -// convert a multi_span to a multi_span -template -multi_span as_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span (a writeable byte multi_span) -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -multi_span as_writeable_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_span(multi_span s) noexcept - -> multi_span( - multi_span::bounds_type::static_size != dynamic_range - ? (static_cast( - multi_span::bounds_type::static_size) / - sizeof(U)) - : dynamic_range)> -{ - using ConstByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ConstByteSpan::bounds_type::static_size == dynamic_range || - ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_span(multi_span s) noexcept -> multi_span< - U, narrow_cast( - multi_span::bounds_type::static_size != dynamic_range - ? static_cast(multi_span::bounds_type::static_size) / - sizeof(U) - : dynamic_range)> -{ - using ByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ByteSpan::bounds_type::static_size == dynamic_range || - ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -template -constexpr auto as_span(T* const& ptr, dim... args) - -> multi_span, Dimensions...> -{ - return {reinterpret_cast*>(ptr), - details::static_as_span_helper>(args..., details::Sep{})}; -} - -template -constexpr auto as_span(T* arr, std::ptrdiff_t len) -> - typename details::SpanArrayTraits::type -{ - return {reinterpret_cast*>(arr), len}; -} - -template -constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type -{ - return {arr}; -} - -template -constexpr multi_span as_span(const std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_span(const std::array&&) = delete; - -template -constexpr multi_span as_span(std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_span(T* begin, T* end) -{ - return {begin, end}; -} - -template -constexpr auto as_span(Cont& arr) -> std::enable_if_t< - !details::is_span>::value, - multi_span, dynamic_range>> -{ - Expects(arr.size() < PTRDIFF_MAX); - return {arr.data(), narrow_cast(arr.size())}; -} - -template -constexpr auto as_span(Cont&& arr) -> std::enable_if_t< - !details::is_span>::value, - multi_span, dynamic_range>> = delete; - -// from basic_string which doesn't have nonconst .data() member like other contiguous containers -template -constexpr auto as_span(std::basic_string& str) - -> multi_span -{ - Expects(str.size() < PTRDIFF_MAX); - return {&str[0], narrow_cast(str.size())}; -} - -// strided_span is an extension that is not strictly part of the GSL at this time. -// It is kept here while the multidimensional interface is still being defined. -template -class strided_span -{ -public: - using bounds_type = strided_bounds; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = general_span_iterator; - using const_strided_span = strided_span; - using const_iterator = general_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - template - friend class strided_span; - -public: - // from raw data - constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) - : data_(ptr), bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); - // Bounds cross data boundaries - Expects(this->bounds().total_size() <= size); - (void) size; - } - - // from static array of size N - template - constexpr strided_span(value_type (&values)[N], bounds_type bounds) - : strided_span(values, N, std::move(bounds)) - { - } - - // from array view - template ::value, - typename Dummy = std::enable_if_t> - constexpr strided_span(multi_span av, bounds_type bounds) - : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - { - } - - // convertible - template ::value>> - constexpr strided_span(const strided_span& other) - : data_(other.data_), bounds_(other.bounds_) - { - } - - // convert from bytes - template - constexpr strided_span< - typename std::enable_if::value, OtherValueType>::type, - Rank> - as_strided_span() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && - (sizeof(OtherValueType) % sizeof(value_type) == 0), - "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return {const_cast(reinterpret_cast(this->data())), size, - bounds_type{resize_extent(this->bounds().index_bounds(), d), - resize_stride(this->bounds().strides(), d)}}; - } - - constexpr strided_span section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - bounds_type{extents, details::make_stride(bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than Rank (dimension count starts from 0)"); - return bounds_.template extent(); - } - - constexpr size_type size() const noexcept { return bounds_.size(); } - - constexpr pointer data() const noexcept { return data_; } - - constexpr explicit operator bool() const noexcept { return data_ != nullptr; } - - constexpr iterator begin() const { return iterator{this, true}; } - - constexpr iterator end() const { return iterator{this, false}; } - - constexpr const_iterator cbegin() const - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } - - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const strided_span& other) const noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const strided_span& other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const strided_span& other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const strided_span& other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const strided_span& other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const strided_span& other) const noexcept - { - return !(*this < other); - } - -private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - // The last dimension of the array needs to contain a multiple of new type elements - Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); - - index_type ret = extent; - ret[Rank - 1] /= d; - - return ret; - } - - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - - return strides; - } - - template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - // The strides must have contiguous chunks of - // memory that can contain a multiple of new type elements - Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - - for (size_t i = Rank - 1; i > 0; --i) { - // Only strided arrays with regular strides can be resized - Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); - } - - index_type ret = strides / d; - ret[Rank - 1] = 1; - - return ret; - } -}; - -template -class contiguous_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - -private: - template - friend class multi_span; - - pointer data_; - const Span* m_validator; - void validateThis() const - { - // iterator is out of range of the array - Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); - } - contiguous_span_iterator(const Span* container, bool isbegin) - : data_(isbegin ? container->data_ : container->data_ + container->size()) - , m_validator(container) - { - } - -public: - reference operator*() const noexcept - { - validateThis(); - return *data_; - } - pointer operator->() const noexcept - { - validateThis(); - return data_; - } - contiguous_span_iterator& operator++() noexcept - { - ++data_; - return *this; - } - contiguous_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_span_iterator& operator--() noexcept - { - --data_; - return *this; - } - contiguous_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_span_iterator operator+(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret += n; - } - contiguous_span_iterator& operator+=(difference_type n) noexcept - { - data_ += n; - return *this; - } - contiguous_span_iterator operator-(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret -= n; - } - contiguous_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } - difference_type operator-(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ - rhs.data_; - } - reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ == rhs.data_; - } - bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ < rhs.data_; - } - bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(contiguous_span_iterator& rhs) noexcept - { - std::swap(data_, rhs.data_); - std::swap(m_validator, rhs.m_validator); - } -}; - -template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, - const contiguous_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -template -class general_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - -private: - template - friend class strided_span; - - const Span* m_container; - typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span* container, bool isbegin) - : m_container(container) - , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } - -public: - reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() noexcept { return &(*m_container)[*m_itr]; } - general_span_iterator& operator++() noexcept - { - ++m_itr; - return *this; - } - general_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - general_span_iterator& operator--() noexcept - { - --m_itr; - return *this; - } - general_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - general_span_iterator operator+(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret += n; - } - general_span_iterator& operator+=(difference_type n) noexcept - { - m_itr += n; - return *this; - } - general_span_iterator operator-(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret -= n; - } - general_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } - difference_type operator-(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const noexcept - { - return (*m_container)[m_itr[n]]; - ; - } - bool operator==(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(general_span_iterator& rhs) noexcept - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } -}; - -template -general_span_iterator operator+(typename general_span_iterator::difference_type n, - const general_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -} // namespace gsl - -#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_VARIADIC_CTOR_BUG - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#undef noexcept - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pop_macro("noexcept") -#endif - -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - -#endif // GSL_SPAN_H diff --git a/include/string_span.h b/include/string_span.h index f585d3f..c58cef9 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -21,7 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" -#include "span.h" +#include "multi_span.h" #include #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7990ec3..87999dc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,7 +33,7 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) endif() function(add_gsl_test name) - add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/span.h ../include/string_span.h) + add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/multi_span.h ../include/string_span.h) target_link_libraries(${name} UnitTest++) install(TARGETS ${name} RUNTIME DESTINATION bin @@ -44,7 +44,7 @@ function(add_gsl_test name) ) endfunction() -add_gsl_test(span_tests) +add_gsl_test(multi_span_tests) add_gsl_test(strided_span_tests) add_gsl_test(string_span_tests) add_gsl_test(at_tests) diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index ab8c5bd..736a85c 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace std; diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp new file mode 100644 index 0000000..207428d --- /dev/null +++ b/tests/multi_span_tests.cpp @@ -0,0 +1,1679 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ +struct BaseClass +{ +}; +struct DerivedClass : BaseClass +{ +}; +} + +SUITE(multi_span_tests) +{ + + TEST(default_constructor) + { + { + multi_span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + multi_span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + multi_span s{}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_constructor) + { + { + multi_span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + multi_span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s = nullptr; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + multi_span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + multi_span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_length_constructor) + { + { + multi_span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + multi_span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{nullptr, 0}; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + auto workaround_macro = []() { multi_span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { multi_span cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { multi_span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { multi_span s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + multi_span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + multi_span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_element_constructor) + { + int i = 5; + + { + multi_span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + + multi_span cs = i; + CHECK(cs.length() == 1 && cs.data() == &i); + CHECK(cs[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + const j = 1; + multi_span s = j; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s = i; + CHECK(s.length() == 0 && s.data() == &i); +#endif + } + + { + multi_span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s = i; + CHECK(s.length() == 2 && s.data() == &i); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_a_temp = []() -> int { return 4; }; + auto use_a_span = [](multi_span s) { (void) s; }; + use_a_span(get_a_temp()); +#endif + } + } + + TEST(from_pointer_length_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + multi_span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + multi_span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + int* p = nullptr; + multi_span s{p, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { multi_span s{p, 2}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_pointer_pointer_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + multi_span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + multi_span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + multi_span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + multi_span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + auto workaround_macro = [&]() { multi_span s{&arr[1], &arr[0]}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { multi_span s{p, p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_array_constructor) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + multi_span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { + multi_span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr}; +#endif + } + + { + multi_span s{arr}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + multi_span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { + multi_span s{arr2d}; + CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr2d}; +#endif + } + + { + multi_span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr2d}; +#endif + } + + { + multi_span s{arr2d[0]}; + CHECK(s.length() == 1 && s.data() == &arr2d[0]); + } + + { + multi_span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr2d}; +#endif + } + + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + { + multi_span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[11] == 12); + } + + { + multi_span s{arr3d}; + CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr3d}; +#endif + } + + { + multi_span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr3d}; +#endif + } + + { + multi_span s{arr3d[0]}; + CHECK(s.length() == 1 && s.data() == &arr3d[0]); + } + + { + multi_span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr3d}; +#endif + } + } + + TEST(from_dynamic_array_constructor) + { + double(*arr)[3][4] = new double[100][3][4]; + + { + multi_span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + CHECK_THROW(s[10][3][4], fail_fast); + } + + { + multi_span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + multi_span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + multi_span s(arr, 0); + CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); + } + + delete[] arr; + } + + TEST(from_std_array_constructor) + { + std::array arr = {1, 2, 3, 4}; + + { + multi_span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + multi_span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + multi_span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + + multi_span cs{arr}; + CHECK(cs.size() == 2 && cs.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + + multi_span cs{arr}; + CHECK(cs.size() == 0 && cs.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // multi_span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; + auto take_a_span = [](multi_span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_const_std_array_constructor) + { + const std::array arr = {1, 2, 3, 4}; + + { + multi_span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + } + + { + multi_span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // multi_span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](multi_span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_container_constructor) + { + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + multi_span s{v}; + CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); + + multi_span cs{v}; + CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{str}; + CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); +#endif + multi_span cs{str}; + CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span s{cstr}; +#endif + multi_span cs{cstr}; + CHECK(cs.size() == narrow_cast(cstr.size()) && + cs.data() == cstr.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](multi_span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](multi_span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> const std::vector { return {}; }; + auto use_span = [](multi_span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](multi_span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map m; + multi_span s{m}; +#endif + } + } + + TEST(from_convertible_span_constructor) + { +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span av1(nullptr, b1); + + auto f = [&]() { multi_span av1(nullptr); }; + CHECK_THROW(f(), fail_fast); +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + multi_span av1 = nullptr; + multi_span av2(av1); + multi_span av2(av1); +#endif + + multi_span avd; +#ifdef CONFIRM_COMPILATION_ERRORS + multi_span avb = avd; +#endif + multi_span avcd = avd; + (void) avcd; + } + + TEST(copy_move_and_assignment) + { + multi_span s1; + CHECK(s1.empty()); + + int arr[] = {3, 4, 5}; + + multi_span s2 = arr; + CHECK(s2.length() == 3 && s2.data() == &arr[0]); + + s2 = s1; + CHECK(s2.empty()); + + auto get_temp_span = [&]() -> multi_span { return {&arr[1], 2}; }; + auto use_span = [&](multi_span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; + use_span(get_temp_span()); + + s1 = get_temp_span(); + CHECK(s1.length() == 2 && s1.data() == &arr[1]); + } + + template + void fn(const Bounds&) + { + static_assert(Bounds::static_size == 60, "static bounds is wrong size"); + } + TEST(as_span_reshape) + { + int a[3][4][5]; + auto av = as_span(a); + fn(av.bounds()); + auto av2 = as_span(av, dim<60>()); + auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); + auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); + auto av6 = as_span(av5, dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = as_bytes(av6); + + auto av8 = as_span(av7); + + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) { + CHECK(av8[i] == 1); + } + } + + TEST(first) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + multi_span av = arr; + CHECK((av.first<2>().bounds() == static_bounds<2>())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + multi_span av = arr; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + multi_span av = arr; + CHECK((av.first<5>().bounds() == static_bounds<5>())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + multi_span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds<6>()); + CHECK(av.first<6>().length() == 6); + CHECK(av.first<-1>().length() == -1); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + multi_span av; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(last) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + multi_span av = arr; + CHECK((av.last<2>().bounds() == static_bounds<2>())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + multi_span av = arr; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + multi_span av = arr; + CHECK((av.last<5>().bounds() == static_bounds<5>())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + { + multi_span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds<6>())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + multi_span av; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(subspan) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + multi_span av = arr; + CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); + CHECK((av.subspan<2, 2>().length() == 2)); + CHECK(av.subspan(2, 2).length() == 2); + CHECK(av.subspan(2, 3).length() == 3); + } + + { + multi_span av = arr; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + } + + { + multi_span av = arr; + CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); + CHECK((av.subspan<0, 5>().length() == 5)); + CHECK(av.subspan(0, 5).length() == 5); + CHECK_THROW(av.subspan(0, 6).length(), fail_fast); + CHECK_THROW(av.subspan(1, 5).length(), fail_fast); + } + + { + multi_span av = arr; + CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<5, 0>().length() == 0)); + CHECK(av.subspan(5, 0).length() == 0); + CHECK_THROW(av.subspan(6, 0).length(), fail_fast); + } + + { + multi_span av; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); + } + + { + multi_span av; + CHECK(av.subspan(0).length() == 0); + CHECK_THROW(av.subspan(1).length(), fail_fast); + } + + { + multi_span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + + { + multi_span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + } + + TEST(rank) + { + int arr[2] = {1, 2}; + + { + multi_span s; + CHECK(s.rank() == 1); + } + + { + multi_span s = arr; + CHECK(s.rank() == 1); + } + + int arr2d[1][1] = {}; + { + multi_span s = arr2d; + CHECK(s.rank() == 2); + } + } + + TEST(extent) + { + { + multi_span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(s.extent<1>() == 0); +#endif + } + + { + multi_span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); + } + + { + int arr2d[1][2] = {}; + + multi_span s = arr2d; + CHECK(s.extent() == 1); + CHECK(s.extent<0>() == 1); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 1); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + + { + int arr2d[1][2] = {}; + + multi_span s = arr2d; + CHECK(s.extent() == 0); + CHECK(s.extent<0>() == 0); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 0); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + } + + TEST(operator_function_call) + { + int arr[4] = {1, 2, 3, 4}; + + { + multi_span s = arr; + CHECK(s(0) == 1); + CHECK_THROW(s(5), fail_fast); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + multi_span s = arr2d; + CHECK(s(0, 0) == 1); + CHECK(s(1, 2) == 6); + } + } + + TEST(comparison_operators) + { + { + int arr[10][2]; + auto s1 = as_span(arr); + multi_span s2 = s1; + + CHECK(s1 == s2); + + multi_span s3 = as_span(s1, dim<>(20)); + CHECK(s3 == s2 && s3 == s1); + } + + { + auto s1 = nullptr; + auto s2 = nullptr; + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {2, 1}; // bigger + + multi_span s1 = nullptr; + multi_span s2 = arr; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + multi_span s1 = arr1; + multi_span s2 = arr2; + + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {1, 2, 3}; + + multi_span s1 = {&arr[0], 2}; // shorter + multi_span s2 = arr; // longer + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + multi_span s1 = arr1; + multi_span s2 = arr2; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + } + + TEST(basics) + { + auto ptr = as_span(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) { + CHECK(num == 99); + } + + delete[] ptr.data(); + } + + TEST(bounds_checks) + { + int arr[10][2]; + auto av = as_span(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10, 2}]), fail_fast); + } + + void overloaded_func(multi_span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void overloaded_func(multi_span exp, char expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void fixed_func(multi_span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + TEST(span_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_span(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + + // fixed_func(av, 34); + delete[] data; + } + + TEST(md_access) + { + auto width = 5, height = 20; + + auto imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = + as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (auto i = 0; i < height; i++) { + for (auto j = 0; j < width; j++) { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(as_span) + { + { + int* arr = new int[150]; + + auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + array stdarr{0}; + auto av2 = as_span(stdarr); + overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + (void) t; + auto av3 = as_span(str); + overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + string str; + multi_span strspan = as_span(str); + (void) strspan; + const string cstr; + multi_span cstrspan = as_span(cstr); + (void) cstrspan; + } + + { + int a[3][4][5]; + auto av = as_span(a); + const int(*b)[4][5]; + b = a; + auto bv = as_span(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_span(arr); + (void) cv; + + vector vec(3); + auto dv = as_span(vec); + (void) dv; + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_span(std::move(vec)); +#endif + } + } + + TEST(empty_spans) + { + { + multi_span empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + + { + multi_span empty_av = {}; + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + multi_span av(arr, 8); + + ptrdiff_t a[1] = {0}; + index<1> i = a; + + CHECK(av[i] == 4); + + auto av2 = as_span(av, dim<4>(), dim<>(2)); + ptrdiff_t a2[2] = {0, 1}; + index<2> i2 = a2; + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = {0, 1, 2}; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = {0}; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = {0}; + index<3> i4 = {0, 1, 2, 3}; + index<1> i5 = {0, 1}; + } +#endif + } + + TEST(index_operations) + { + ptrdiff_t a[3] = {0, 1, 2}; + ptrdiff_t b[3] = {3, 4, 5}; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + } + + void iterate_second_column(multi_span av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({0, 1}, {length, 1}); + + CHECK(section.size() == length); + for (auto i = 0; i < section.size(); ++i) { + CHECK(section[i][0] == av[i][1]); + } + + for (auto i = 0; i < section.size(); ++i) { + auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { + auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + } + + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) { + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); + } + } + + TEST(span_section_iteration) + { + int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; + + // static bounds + { + multi_span av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + multi_span av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + multi_span av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + multi_span av = arr; + iterate_second_column(av); + } + } + + TEST(dynamic_span_section_iteration) + { + auto height = 4, 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(arr, size); + + // first bound is dynamic + { + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + TEST(span_structure_size) + { + double(*arr)[3][4] = new double[100][3][4]; + multi_span av1(arr, 10); + + struct EffectiveStructure + { + double* v1; + ptrdiff_t v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + multi_span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + (void) av2; + } + + TEST(fixed_size_conversions) + { + int arr[] = {1, 2, 3, 4}; + + // converting to an multi_span from an equal size array is ok + multi_span av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + multi_span av = av4; + (void) av; + } + { + multi_span av = arr; + (void) av; + } + +// initialization or assignment to static multi_span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + multi_span av2 = arr; + } + { + multi_span av2 = av4; + } +#endif + + { + multi_span av = arr; + multi_span av2 = av; + (void) av2; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + multi_span av = arr; + multi_span av2 = av.as_span(dim<2>(), dim<2>()); + } +#endif + + { + multi_span av = arr; + multi_span av2 = as_span(av, dim<>(2), dim<>(2)); + auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; + CHECK(workaround_macro()); + } + + // but doing so explicitly is ok + + // you can convert statically + { + multi_span av2 = {arr, 2}; + (void) av2; + } + { + multi_span av2 = av4.first<1>(); + (void) av2; + } + + // ...or dynamically + { + // NB: implicit conversion to multi_span from multi_span + multi_span av2 = av4.first(1); + (void) av2; + } + + // initialization or assignment to static multi_span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + multi_span av4 = arr2; + } + { + multi_span av2 = arr2; + multi_span av4 = av2; + } +#endif + { + auto f = [&]() { + multi_span av9 = {arr2, 2}; + (void) av9; + }; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + multi_span av = arr2; + auto f = [&]() { + multi_span av2 = av; + (void) av2; + }; + CHECK_THROW(f(), fail_fast); + } + + TEST(as_writeable_bytes) + { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + multi_span av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + multi_span av; + auto wav = as_writeable_bytes(av); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.size_bytes() == 0); + } + + { + multi_span av = a; + auto wav = as_writeable_bytes(av); + CHECK(wav.data() == (byte*) &a[0]); + CHECK(wav.length() == sizeof(a)); + } + } + + TEST(iterator) + { + int a[] = {1, 2, 3, 4}; + + { + multi_span av = a; + auto wav = as_writeable_bytes(av); + for (auto& b : wav) { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 0); + } + } + + { + multi_span av = a; + for (auto& n : av) { + n = 1; + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 1); + } + } + } +} + +int main(int, const char* []) { return UnitTest::RunAllTests(); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp deleted file mode 100644 index 0d1170d..0000000 --- a/tests/span_tests.cpp +++ /dev/null @@ -1,1679 +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 -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace gsl; - -namespace -{ -struct BaseClass -{ -}; -struct DerivedClass : BaseClass -{ -}; -} - -SUITE(span_tests) -{ - - TEST(default_constructor) - { - { - multi_span s; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { - multi_span s; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s; - CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile -#endif - } - - { - multi_span s{}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - } - - TEST(from_nullptr_constructor) - { - { - multi_span s = nullptr; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs = nullptr; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { - multi_span s = nullptr; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs = nullptr; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s = nullptr; - CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile -#endif - } - - { - multi_span s{nullptr}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{nullptr}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { - multi_span s{nullptr}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{nullptr}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - } - - TEST(from_nullptr_length_constructor) - { - { - multi_span s{nullptr, 0}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{nullptr, 0}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { - multi_span s{nullptr, 0}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{nullptr, 0}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{nullptr, 0}; - CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile -#endif - } - - { - auto workaround_macro = []() { multi_span s{nullptr, 1}; }; - CHECK_THROW(workaround_macro(), fail_fast); - - auto const_workaround_macro = []() { multi_span cs{nullptr, 1}; }; - CHECK_THROW(const_workaround_macro(), fail_fast); - } - - { - auto workaround_macro = []() { multi_span s{nullptr, 1}; }; - CHECK_THROW(workaround_macro(), fail_fast); - - auto const_workaround_macro = []() { multi_span s{nullptr, 1}; }; - CHECK_THROW(const_workaround_macro(), fail_fast); - } - - { - multi_span s{nullptr, 0}; - CHECK(s.length() == 0 && s.data() == nullptr); - - multi_span cs{nullptr, 0}; - CHECK(cs.length() == 0 && cs.data() == nullptr); - } - } - - TEST(from_element_constructor) - { - int i = 5; - - { - multi_span s = i; - CHECK(s.length() == 1 && s.data() == &i); - CHECK(s[0] == 5); - - multi_span cs = i; - CHECK(cs.length() == 1 && cs.data() == &i); - CHECK(cs[0] == 5); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - const j = 1; - multi_span s = j; -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s = i; - CHECK(s.length() == 0 && s.data() == &i); -#endif - } - - { - multi_span s = i; - CHECK(s.length() == 1 && s.data() == &i); - CHECK(s[0] == 5); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s = i; - CHECK(s.length() == 2 && s.data() == &i); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_a_temp = []() -> int { return 4; }; - auto use_a_span = [](multi_span s) { (void) s; }; - use_a_span(get_a_temp()); -#endif - } - } - - TEST(from_pointer_length_constructor) - { - int arr[4] = {1, 2, 3, 4}; - - { - multi_span s{&arr[0], 2}; - CHECK(s.length() == 2 && s.data() == &arr[0]); - CHECK(s[0] == 1 && s[1] == 2); - } - - { - multi_span s{&arr[0], 2}; - CHECK(s.length() == 2 && s.data() == &arr[0]); - CHECK(s[0] == 1 && s[1] == 2); - } - - { - int* p = nullptr; - multi_span s{p, 0}; - CHECK(s.length() == 0 && s.data() == nullptr); - } - - { - int* p = nullptr; - auto workaround_macro = [=]() { multi_span s{p, 2}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } - } - - TEST(from_pointer_pointer_constructor) - { - int arr[4] = {1, 2, 3, 4}; - - { - multi_span s{&arr[0], &arr[2]}; - CHECK(s.length() == 2 && s.data() == &arr[0]); - CHECK(s[0] == 1 && s[1] == 2); - } - - { - multi_span s{&arr[0], &arr[2]}; - CHECK(s.length() == 2 && s.data() == &arr[0]); - CHECK(s[0] == 1 && s[1] == 2); - } - - { - multi_span s{&arr[0], &arr[0]}; - CHECK(s.length() == 0 && s.data() == &arr[0]); - } - - { - multi_span s{&arr[0], &arr[0]}; - CHECK(s.length() == 0 && s.data() == &arr[0]); - } - - { - auto workaround_macro = [&]() { multi_span s{&arr[1], &arr[0]}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } - - { - int* p = nullptr; - auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } - - { - int* p = nullptr; - auto workaround_macro = [&]() { multi_span s{p, p}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } - - { - int* p = nullptr; - auto workaround_macro = [&]() { multi_span s{&arr[0], p}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } - } - - TEST(from_array_constructor) - { - int arr[5] = {1, 2, 3, 4, 5}; - - { - multi_span s{arr}; - CHECK(s.length() == 5 && s.data() == &arr[0]); - } - - { - multi_span s{arr}; - CHECK(s.length() == 5 && s.data() == &arr[0]); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr}; -#endif - } - - { - multi_span s{arr}; - CHECK(s.length() == 0 && s.data() == &arr[0]); - } - - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - - { - multi_span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - CHECK(s[0] == 1 && s[5] == 6); - } - - { - multi_span s{arr2d}; - CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr2d}; -#endif - } - - { - multi_span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - CHECK(s[0] == 1 && s[5] == 6); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr2d}; -#endif - } - - { - multi_span s{arr2d[0]}; - CHECK(s.length() == 1 && s.data() == &arr2d[0]); - } - - { - multi_span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr2d}; -#endif - } - - int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - - { - multi_span s{arr3d}; - CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); - CHECK(s[0] == 1 && s[11] == 12); - } - - { - multi_span s{arr3d}; - CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr3d}; -#endif - } - - { - multi_span s{arr3d}; - CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); - CHECK(s[0] == 1 && s[5] == 6); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr3d}; -#endif - } - - { - multi_span s{arr3d[0]}; - CHECK(s.length() == 1 && s.data() == &arr3d[0]); - } - - { - multi_span s{arr3d}; - CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); - auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr3d}; -#endif - } - } - - TEST(from_dynamic_array_constructor) - { - double(*arr)[3][4] = new double[100][3][4]; - - { - multi_span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - CHECK_THROW(s[10][3][4], fail_fast); - } - - { - multi_span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - } - - { - multi_span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - } - - { - multi_span s(arr, 0); - CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); - } - - delete[] arr; - } - - TEST(from_std_array_constructor) - { - std::array arr = {1, 2, 3, 4}; - - { - multi_span s{arr}; - CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - - multi_span cs{arr}; - CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - - multi_span cs{arr}; - CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == 2 && s.data() == arr.data()); - - multi_span cs{arr}; - CHECK(cs.size() == 2 && cs.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == 0 && s.data() == arr.data()); - - multi_span cs{arr}; - CHECK(cs.size() == 0 && cs.data() == arr.data()); - } - - // TODO This is currently an unsupported scenario. We will come back to it as we revise - // the multidimensional interface and what transformations between dimensionality look like - //{ - // multi_span s{arr}; - // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - //} - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr}; -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; - auto take_a_span = [](multi_span s) { (void) s; }; - // try to take a temporary std::array - take_a_span(get_an_array()); -#endif - } - } - - TEST(from_const_std_array_constructor) - { - const std::array arr = {1, 2, 3, 4}; - - { - multi_span s{arr}; - CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == 2 && s.data() == arr.data()); - } - - { - multi_span s{arr}; - CHECK(s.size() == 0 && s.data() == arr.data()); - } - - // TODO This is currently an unsupported scenario. We will come back to it as we revise - // the multidimensional interface and what transformations between dimensionality look like - //{ - // multi_span s{arr}; - // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - //} - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{arr}; -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](multi_span s) { (void) s; }; - // try to take a temporary std::array - take_a_span(get_an_array()); -#endif - } - } - - TEST(from_container_constructor) - { - std::vector v = {1, 2, 3}; - const std::vector cv = v; - - { - multi_span s{v}; - CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); - - multi_span cs{v}; - CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); - } - - std::string str = "hello"; - const std::string cstr = "hello"; - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{str}; - CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); -#endif - multi_span cs{str}; - CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span s{cstr}; -#endif - multi_span cs{cstr}; - CHECK(cs.size() == narrow_cast(cstr.size()) && - cs.data() == cstr.data()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](multi_span s) { (void) s; }; - use_span(get_temp_vector()); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](multi_span s) { (void) s; }; - use_span(get_temp_string()); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_vector = []() -> const std::vector { return {}; }; - auto use_span = [](multi_span s) { (void) s; }; - use_span(get_temp_vector()); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_string = []() -> const std::string { return {}; }; - auto use_span = [](multi_span s) { (void) s; }; - use_span(get_temp_string()); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - std::map m; - multi_span s{m}; -#endif - } - } - - TEST(from_convertible_span_constructor) - { -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span av1(nullptr, b1); - - auto f = [&]() { multi_span av1(nullptr); }; - CHECK_THROW(f(), fail_fast); -#endif - -#ifdef CONFIRM_COMPILATION_ERRORS - static_bounds b12(b11); - b12 = b11; - b11 = b12; - - multi_span av1 = nullptr; - multi_span av2(av1); - multi_span av2(av1); -#endif - - multi_span avd; -#ifdef CONFIRM_COMPILATION_ERRORS - multi_span avb = avd; -#endif - multi_span avcd = avd; - (void) avcd; - } - - TEST(copy_move_and_assignment) - { - multi_span s1; - CHECK(s1.empty()); - - int arr[] = {3, 4, 5}; - - multi_span s2 = arr; - CHECK(s2.length() == 3 && s2.data() == &arr[0]); - - s2 = s1; - CHECK(s2.empty()); - - auto get_temp_span = [&]() -> multi_span { return {&arr[1], 2}; }; - auto use_span = [&](multi_span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; - use_span(get_temp_span()); - - s1 = get_temp_span(); - CHECK(s1.length() == 2 && s1.data() == &arr[1]); - } - - template - void fn(const Bounds&) - { - static_assert(Bounds::static_size == 60, "static bounds is wrong size"); - } - TEST(as_span_reshape) - { - int a[3][4][5]; - auto av = as_span(a); - fn(av.bounds()); - auto av2 = as_span(av, dim<60>()); - auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); - auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); - auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); - auto av6 = as_span(av5, dim<12>(), dim<>(5)); - - fill(av6.begin(), av6.end(), 1); - - auto av7 = as_bytes(av6); - - auto av8 = as_span(av7); - - CHECK(av8.size() == av6.size()); - for (auto i = 0; i < av8.size(); i++) { - CHECK(av8[i] == 1); - } - } - - TEST(first) - { - int arr[5] = {1, 2, 3, 4, 5}; - - { - multi_span av = arr; - CHECK((av.first<2>().bounds() == static_bounds<2>())); - CHECK(av.first<2>().length() == 2); - CHECK(av.first(2).length() == 2); - } - - { - multi_span av = arr; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - - { - multi_span av = arr; - CHECK((av.first<5>().bounds() == static_bounds<5>())); - CHECK(av.first<5>().length() == 5); - CHECK(av.first(5).length() == 5); - } - - { - multi_span av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK(av.first<6>().bounds() == static_bounds<6>()); - CHECK(av.first<6>().length() == 6); - CHECK(av.first<-1>().length() == -1); -#endif - CHECK_THROW(av.first(6).length(), fail_fast); - } - - { - multi_span av; - CHECK((av.first<0>().bounds() == static_bounds<0>())); - CHECK(av.first<0>().length() == 0); - CHECK(av.first(0).length() == 0); - } - } - - TEST(last) - { - int arr[5] = {1, 2, 3, 4, 5}; - - { - multi_span av = arr; - CHECK((av.last<2>().bounds() == static_bounds<2>())); - CHECK(av.last<2>().length() == 2); - CHECK(av.last(2).length() == 2); - } - - { - multi_span av = arr; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - - { - multi_span av = arr; - CHECK((av.last<5>().bounds() == static_bounds<5>())); - CHECK(av.last<5>().length() == 5); - CHECK(av.last(5).length() == 5); - } - - { - multi_span av = arr; -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK((av.last<6>().bounds() == static_bounds<6>())); - CHECK(av.last<6>().length() == 6); -#endif - CHECK_THROW(av.last(6).length(), fail_fast); - } - - { - multi_span av; - CHECK((av.last<0>().bounds() == static_bounds<0>())); - CHECK(av.last<0>().length() == 0); - CHECK(av.last(0).length() == 0); - } - } - - TEST(subspan) - { - int arr[5] = {1, 2, 3, 4, 5}; - - { - multi_span av = arr; - CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); - CHECK((av.subspan<2, 2>().length() == 2)); - CHECK(av.subspan(2, 2).length() == 2); - CHECK(av.subspan(2, 3).length() == 3); - } - - { - multi_span av = arr; - CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); - CHECK((av.subspan<0, 0>().length() == 0)); - CHECK(av.subspan(0, 0).length() == 0); - } - - { - multi_span av = arr; - CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); - CHECK((av.subspan<0, 5>().length() == 5)); - CHECK(av.subspan(0, 5).length() == 5); - CHECK_THROW(av.subspan(0, 6).length(), fail_fast); - CHECK_THROW(av.subspan(1, 5).length(), fail_fast); - } - - { - multi_span av = arr; - CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); - CHECK((av.subspan<5, 0>().length() == 0)); - CHECK(av.subspan(5, 0).length() == 0); - CHECK_THROW(av.subspan(6, 0).length(), fail_fast); - } - - { - multi_span av; - CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); - CHECK((av.subspan<0, 0>().length() == 0)); - CHECK(av.subspan(0, 0).length() == 0); - CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); - } - - { - multi_span av; - CHECK(av.subspan(0).length() == 0); - CHECK_THROW(av.subspan(1).length(), fail_fast); - } - - { - multi_span av = arr; - CHECK(av.subspan(0).length() == 5); - CHECK(av.subspan(1).length() == 4); - CHECK(av.subspan(4).length() == 1); - CHECK(av.subspan(5).length() == 0); - CHECK_THROW(av.subspan(6).length(), fail_fast); - auto av2 = av.subspan(1); - for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); - } - - { - multi_span av = arr; - CHECK(av.subspan(0).length() == 5); - CHECK(av.subspan(1).length() == 4); - CHECK(av.subspan(4).length() == 1); - CHECK(av.subspan(5).length() == 0); - CHECK_THROW(av.subspan(6).length(), fail_fast); - auto av2 = av.subspan(1); - for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); - } - } - - TEST(rank) - { - int arr[2] = {1, 2}; - - { - multi_span s; - CHECK(s.rank() == 1); - } - - { - multi_span s = arr; - CHECK(s.rank() == 1); - } - - int arr2d[1][1] = {}; - { - multi_span s = arr2d; - CHECK(s.rank() == 2); - } - } - - TEST(extent) - { - { - multi_span s; - CHECK(s.extent() == 0); - CHECK(s.extent(0) == 0); - CHECK_THROW(s.extent(1), fail_fast); -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK(s.extent<1>() == 0); -#endif - } - - { - multi_span s; - CHECK(s.extent() == 0); - CHECK(s.extent(0) == 0); - CHECK_THROW(s.extent(1), fail_fast); - } - - { - int arr2d[1][2] = {}; - - multi_span s = arr2d; - CHECK(s.extent() == 1); - CHECK(s.extent<0>() == 1); - CHECK(s.extent<1>() == 2); - CHECK(s.extent(0) == 1); - CHECK(s.extent(1) == 2); - CHECK_THROW(s.extent(3), fail_fast); - } - - { - int arr2d[1][2] = {}; - - multi_span s = arr2d; - CHECK(s.extent() == 0); - CHECK(s.extent<0>() == 0); - CHECK(s.extent<1>() == 2); - CHECK(s.extent(0) == 0); - CHECK(s.extent(1) == 2); - CHECK_THROW(s.extent(3), fail_fast); - } - } - - TEST(operator_function_call) - { - int arr[4] = {1, 2, 3, 4}; - - { - multi_span s = arr; - CHECK(s(0) == 1); - CHECK_THROW(s(5), fail_fast); - } - - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - - { - multi_span s = arr2d; - CHECK(s(0, 0) == 1); - CHECK(s(1, 2) == 6); - } - } - - TEST(comparison_operators) - { - { - int arr[10][2]; - auto s1 = as_span(arr); - multi_span s2 = s1; - - CHECK(s1 == s2); - - multi_span s3 = as_span(s1, dim<>(20)); - CHECK(s3 == s2 && s3 == s1); - } - - { - auto s1 = nullptr; - auto s2 = nullptr; - CHECK(s1 == s2); - CHECK(!(s1 != s2)); - CHECK(!(s1 < s2)); - CHECK(s1 <= s2); - CHECK(!(s1 > s2)); - CHECK(s1 >= s2); - CHECK(s2 == s1); - CHECK(!(s2 != s1)); - CHECK(!(s2 < s1)); - CHECK(s2 <= s1); - CHECK(!(s2 > s1)); - CHECK(s2 >= s1); - } - - { - int arr[] = {2, 1}; // bigger - - multi_span s1 = nullptr; - multi_span s2 = arr; - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } - - { - int arr1[] = {1, 2}; - int arr2[] = {1, 2}; - multi_span s1 = arr1; - multi_span s2 = arr2; - - CHECK(s1 == s2); - CHECK(!(s1 != s2)); - CHECK(!(s1 < s2)); - CHECK(s1 <= s2); - CHECK(!(s1 > s2)); - CHECK(s1 >= s2); - CHECK(s2 == s1); - CHECK(!(s2 != s1)); - CHECK(!(s2 < s1)); - CHECK(s2 <= s1); - CHECK(!(s2 > s1)); - CHECK(s2 >= s1); - } - - { - int arr[] = {1, 2, 3}; - - multi_span s1 = {&arr[0], 2}; // shorter - multi_span s2 = arr; // longer - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } - - { - int arr1[] = {1, 2}; // smaller - int arr2[] = {2, 1}; // bigger - - multi_span s1 = arr1; - multi_span s2 = arr2; - - CHECK(s1 != s2); - CHECK(s2 != s1); - CHECK(!(s1 == s2)); - CHECK(!(s2 == s1)); - CHECK(s1 < s2); - CHECK(!(s2 < s1)); - CHECK(s1 <= s2); - CHECK(!(s2 <= s1)); - CHECK(s2 > s1); - CHECK(!(s1 > s2)); - CHECK(s2 >= s1); - CHECK(!(s1 >= s2)); - } - } - - TEST(basics) - { - auto ptr = as_span(new int[10], 10); - fill(ptr.begin(), ptr.end(), 99); - for (int num : ptr) { - CHECK(num == 99); - } - - delete[] ptr.data(); - } - - TEST(bounds_checks) - { - int arr[10][2]; - auto av = as_span(arr); - - fill(begin(av), end(av), 0); - - av[2][0] = 1; - av[1][1] = 3; - - // out of bounds - CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[{1, 3}] = 3), fail_fast); - - CHECK_THROW(av[10][2], fail_fast); - CHECK_THROW((av[{10, 2}]), fail_fast); - } - - void overloaded_func(multi_span exp, int expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - void overloaded_func(multi_span exp, char expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - void fixed_func(multi_span exp, int expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - TEST(span_parameter_test) - { - auto data = new int[4][3][5]; - - auto av = as_span(data, 4); - - CHECK(av.size() == 60); - - fill(av.begin(), av.end(), 34); - - int count = 0; - for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); - CHECK(count == 34 * 60); - overloaded_func(av, 34); - - overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); - - // fixed_func(av, 34); - delete[] data; - } - - TEST(md_access) - { - auto width = 5, height = 20; - - auto imgSize = width * height; - auto image_ptr = new int[imgSize][3]; - - // size check will be done - auto image_view = - as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); - - iota(image_view.begin(), image_view.end(), 1); - - int expected = 0; - for (auto i = 0; i < height; i++) { - for (auto j = 0; j < width; j++) { - CHECK(expected + 1 == image_view[i][j][0]); - CHECK(expected + 2 == image_view[i][j][1]); - CHECK(expected + 3 == image_view[i][j][2]); - - auto val = image_view[{i, j, 0}]; - CHECK(expected + 1 == val); - val = image_view[{i, j, 1}]; - CHECK(expected + 2 == val); - val = image_view[{i, j, 2}]; - CHECK(expected + 3 == val); - - expected += 3; - } - } - } - - TEST(as_span) - { - { - int* arr = new int[150]; - - auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); - - fill(av.begin(), av.end(), 24); - overloaded_func(av, 24); - - delete[] arr; - - array stdarr{0}; - auto av2 = as_span(stdarr); - overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); - - string str = "ttttttttttttttt"; // size = 15 - auto t = str.data(); - (void) t; - auto av3 = as_span(str); - overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); - } - - { - string str; - multi_span strspan = as_span(str); - (void) strspan; - const string cstr; - multi_span cstrspan = as_span(cstr); - (void) cstrspan; - } - - { - int a[3][4][5]; - auto av = as_span(a); - const int(*b)[4][5]; - b = a; - auto bv = as_span(b, 3); - - CHECK(av == bv); - - const std::array arr = {0.0, 0.0, 0.0}; - auto cv = as_span(arr); - (void) cv; - - vector vec(3); - auto dv = as_span(vec); - (void) dv; - -#ifdef CONFIRM_COMPILATION_ERRORS - auto dv2 = as_span(std::move(vec)); -#endif - } - } - - TEST(empty_spans) - { - { - multi_span empty_av(nullptr); - - CHECK(empty_av.bounds().index_bounds() == index<1>{0}); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) { - (void) v; - CHECK(false); - } - } - - { - multi_span empty_av = {}; - CHECK(empty_av.bounds().index_bounds() == index<1>{0}); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) { - (void) v; - CHECK(false); - } - } - } - - TEST(index_constructor) - { - auto arr = new int[8]; - for (int i = 0; i < 4; ++i) { - arr[2 * i] = 4 + i; - arr[2 * i + 1] = i; - } - - multi_span av(arr, 8); - - ptrdiff_t a[1] = {0}; - index<1> i = a; - - CHECK(av[i] == 4); - - auto av2 = as_span(av, dim<4>(), dim<>(2)); - ptrdiff_t a2[2] = {0, 1}; - index<2> i2 = a2; - - CHECK(av2[i2] == 0); - CHECK(av2[0][i] == 4); - - delete[] arr; - } - - TEST(index_constructors) - { - { - // components of the same type - index<3> i1(0, 1, 2); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - size_t c1 = 1; - index<3> i2(c0, c1, 2); - CHECK(i2[0] == 0); - - // from array - index<3> i3 = {0, 1, 2}; - CHECK(i3[0] == 0); - - // from other index of the same size type - index<3> i4 = i3; - CHECK(i4[0] == 0); - - // default - index<3> i7; - CHECK(i7[0] == 0); - - // default - index<3> i9 = {}; - CHECK(i9[0] == 0); - } - - { - // components of the same type - index<1> i1(0); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - index<1> i2(c0); - CHECK(i2[0] == 0); - - // from array - index<1> i3 = {0}; - CHECK(i3[0] == 0); - - // from int - index<1> i4 = 0; - CHECK(i4[0] == 0); - - // from other index of the same size type - index<1> i5 = i3; - CHECK(i5[0] == 0); - - // default - index<1> i8; - CHECK(i8[0] == 0); - - // default - index<1> i9 = {}; - CHECK(i9[0] == 0); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - index<3> i1(0, 1); - index<3> i2(0, 1, 2, 3); - index<3> i3 = {0}; - index<3> i4 = {0, 1, 2, 3}; - index<1> i5 = {0, 1}; - } -#endif - } - - TEST(index_operations) - { - ptrdiff_t a[3] = {0, 1, 2}; - ptrdiff_t b[3] = {3, 4, 5}; - index<3> i = a; - index<3> j = b; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - - { - index<3> k = i + j; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 3); - CHECK(k[1] == 5); - CHECK(k[2] == 7); - } - - { - index<3> k = i * 3; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<3> k = 3 * i; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<2> k = details::shift_left(i); - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 1); - CHECK(k[1] == 2); - } - } - - void iterate_second_column(multi_span av) - { - auto length = av.size() / 2; - - // view to the second column - auto section = av.section({0, 1}, {length, 1}); - - CHECK(section.size() == length); - for (auto i = 0; i < section.size(); ++i) { - CHECK(section[i][0] == av[i][1]); - } - - for (auto i = 0; i < section.size(); ++i) { - auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - - CHECK(section.bounds().index_bounds()[0] == length); - CHECK(section.bounds().index_bounds()[1] == 1); - for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { - for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { - auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - } - - size_t check_sum = 0; - for (auto i = 0; i < length; ++i) { - check_sum += av[i][1]; - } - - { - auto idx = 0; - size_t sum = 0; - for (auto num : section) { - CHECK(num == av[idx][1]); - sum += num; - idx++; - } - - CHECK(sum == check_sum); - } - { - size_t idx = length - 1; - size_t sum = 0; - for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { - CHECK(*iter == av[idx][1]); - sum += *iter; - idx--; - } - - CHECK(sum == check_sum); - } - } - - TEST(span_section_iteration) - { - int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; - - // static bounds - { - multi_span av = arr; - iterate_second_column(av); - } - // first bound is dynamic - { - multi_span av = arr; - iterate_second_column(av); - } - // second bound is dynamic - { - multi_span av = arr; - iterate_second_column(av); - } - // both bounds are dynamic - { - multi_span av = arr; - iterate_second_column(av); - } - } - - TEST(dynamic_span_section_iteration) - { - auto height = 4, 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(arr, size); - - // first bound is dynamic - { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // second bound is dynamic - { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // both bounds are dynamic - { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - - delete[] arr; - } - - TEST(span_structure_size) - { - double(*arr)[3][4] = new double[100][3][4]; - multi_span av1(arr, 10); - - struct EffectiveStructure - { - double* v1; - ptrdiff_t v2; - }; - CHECK(sizeof(av1) == sizeof(EffectiveStructure)); - - CHECK_THROW(av1[10][3][4], fail_fast); - - multi_span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); - (void) av2; - } - - TEST(fixed_size_conversions) - { - int arr[] = {1, 2, 3, 4}; - - // converting to an multi_span from an equal size array is ok - multi_span av4 = arr; - CHECK(av4.length() == 4); - - // converting to dynamic_range a_v is always ok - { - multi_span av = av4; - (void) av; - } - { - multi_span av = arr; - (void) av; - } - -// initialization or assignment to static multi_span that REDUCES size is NOT ok -#ifdef CONFIRM_COMPILATION_ERRORS - { - multi_span av2 = arr; - } - { - multi_span av2 = av4; - } -#endif - - { - multi_span av = arr; - multi_span av2 = av; - (void) av2; - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - multi_span av = arr; - multi_span av2 = av.as_span(dim<2>(), dim<2>()); - } -#endif - - { - multi_span av = arr; - multi_span av2 = as_span(av, dim<>(2), dim<>(2)); - auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; - CHECK(workaround_macro()); - } - - // but doing so explicitly is ok - - // you can convert statically - { - multi_span av2 = {arr, 2}; - (void) av2; - } - { - multi_span av2 = av4.first<1>(); - (void) av2; - } - - // ...or dynamically - { - // NB: implicit conversion to multi_span from multi_span - multi_span av2 = av4.first(1); - (void) av2; - } - - // initialization or assignment to static multi_span that requires size INCREASE is not ok. - int arr2[2] = {1, 2}; - -#ifdef CONFIRM_COMPILATION_ERRORS - { - multi_span av4 = arr2; - } - { - multi_span av2 = arr2; - multi_span av4 = av2; - } -#endif - { - auto f = [&]() { - multi_span av9 = {arr2, 2}; - (void) av9; - }; - CHECK_THROW(f(), fail_fast); - } - - // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one - multi_span av = arr2; - auto f = [&]() { - multi_span av2 = av; - (void) av2; - }; - CHECK_THROW(f(), fail_fast); - } - - TEST(as_writeable_bytes) - { - int a[] = {1, 2, 3, 4}; - - { -#ifdef CONFIRM_COMPILATION_ERRORS - // you should not be able to get writeable bytes for const objects - multi_span av = a; - auto wav = av.as_writeable_bytes(); -#endif - } - - { - multi_span av; - auto wav = as_writeable_bytes(av); - CHECK(wav.length() == av.length()); - CHECK(wav.length() == 0); - CHECK(wav.size_bytes() == 0); - } - - { - multi_span av = a; - auto wav = as_writeable_bytes(av); - CHECK(wav.data() == (byte*) &a[0]); - CHECK(wav.length() == sizeof(a)); - } - } - - TEST(iterator) - { - int a[] = {1, 2, 3, 4}; - - { - multi_span av = a; - auto wav = as_writeable_bytes(av); - for (auto& b : wav) { - b = byte(0); - } - for (size_t i = 0; i < 4; ++i) { - CHECK(a[i] == 0); - } - } - - { - multi_span av = a; - for (auto& n : av) { - n = 1; - } - for (size_t i = 0; i < 4; ++i) { - CHECK(a[i] == 1); - } - } - } -} - -int main(int, const char* []) { return UnitTest::RunAllTests(); } diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index 8b96ddf..9fcdfeb 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include -- cgit v1.2.3 From cec26a23b93491f564a7c38e66fee73a8cc1e86c Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 24 Feb 2016 11:26:28 -0800 Subject: Added new span-related files. --- include/gsl.h | 2 +- include/multi_span.h | 6 +- include/span.h | 115 ++++ tests/CMakeLists.txt | 3 +- tests/span_tests.cpp | 1680 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1801 insertions(+), 5 deletions(-) create mode 100644 include/span.h create mode 100644 tests/span_tests.cpp diff --git a/include/gsl.h b/include/gsl.h index 03f8545..4eb555b 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -21,7 +21,7 @@ #include "gsl_assert.h" // Ensures/Expects #include "gsl_util.h" // finally()/narrow()/narrow_cast()... -//#include "span.h" // span +#include "span.h" // span #include "multi_span.h" // multi_span, strided_span... #include "string_span.h" // zstring, string_span, zstring_builder... #include diff --git a/include/multi_span.h b/include/multi_span.h index 659e116..b070f29 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -16,8 +16,8 @@ #pragma once -#ifndef GSL_SPAN_H -#define GSL_SPAN_H +#ifndef GSL_MULTI_SPAN_H +#define GSL_MULTI_SPAN_H #include "gsl_assert.h" #include "gsl_util.h" @@ -2221,4 +2221,4 @@ general_span_iterator operator+(typename general_span_iterator::diff #endif // GSL_THROW_ON_CONTRACT_VIOLATION -#endif // GSL_SPAN_H +#endif // GSL_MULTI_SPAN_H diff --git a/include/span.h b/include/span.h new file mode 100644 index 0000000..7df4964 --- /dev/null +++ b/include/span.h @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include "gsl_assert.h" +#include "gsl_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + + +} // namespace gsl + +#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_VARIADIC_CTOR_BUG + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#endif // GSL_SPAN_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 87999dc..9723309 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,7 +33,7 @@ if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) endif() function(add_gsl_test name) - add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/multi_span.h ../include/string_span.h) + add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/multi_span.h ../include/span.h ../include/string_span.h) target_link_libraries(${name} UnitTest++) install(TARGETS ${name} RUNTIME DESTINATION bin @@ -44,6 +44,7 @@ function(add_gsl_test name) ) endfunction() +add_gsl_test(span_tests) add_gsl_test(multi_span_tests) add_gsl_test(strided_span_tests) add_gsl_test(string_span_tests) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp new file mode 100644 index 0000000..83eec0e --- /dev/null +++ b/tests/span_tests.cpp @@ -0,0 +1,1680 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ +struct BaseClass +{ +}; +struct DerivedClass : BaseClass +{ +}; +} + +SUITE(span_tests) +{ +#if 0 + TEST(default_constructor) + { + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + span s{}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_constructor) + { + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s = nullptr; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs = nullptr; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = nullptr; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_nullptr_length_constructor) + { + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{nullptr, 0}; + CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile +#endif + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span cs{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + auto workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(workaround_macro(), fail_fast); + + auto const_workaround_macro = []() { span s{nullptr, 1}; }; + CHECK_THROW(const_workaround_macro(), fail_fast); + } + + { + span s{nullptr, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + + span cs{nullptr, 0}; + CHECK(cs.length() == 0 && cs.data() == nullptr); + } + } + + TEST(from_element_constructor) + { + int i = 5; + + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + + span cs = i; + CHECK(cs.length() == 1 && cs.data() == &i); + CHECK(cs[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + const j = 1; + span s = j; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = i; + CHECK(s.length() == 0 && s.data() == &i); +#endif + } + + { + span s = i; + CHECK(s.length() == 1 && s.data() == &i); + CHECK(s[0] == 5); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s = i; + CHECK(s.length() == 2 && s.data() == &i); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_a_temp = []() -> int { return 4; }; + auto use_a_span = [](span s) { (void) s; }; + use_a_span(get_a_temp()); +#endif + } + } + + TEST(from_pointer_length_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], 2}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + int* p = nullptr; + span s{p, 0}; + CHECK(s.length() == 0 && s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { span s{p, 2}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_pointer_pointer_constructor) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[2]}; + CHECK(s.length() == 2 && s.data() == &arr[0]); + CHECK(s[0] == 1 && s[1] == 2); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + span s{&arr[0], &arr[0]}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + { + auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{p, p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + + { + int* p = nullptr; + auto workaround_macro = [&]() { span s{&arr[0], p}; }; + CHECK_THROW(workaround_macro(), fail_fast); + } + } + + TEST(from_array_constructor) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { + span s{arr}; + CHECK(s.length() == 5 && s.data() == &arr[0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { + span s{arr}; + CHECK(s.length() == 0 && s.data() == &arr[0]); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { + span s{arr2d}; + CHECK(s.length() == 0 && s.data() == &arr2d[0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + { + span s{arr2d[0]}; + CHECK(s.length() == 1 && s.data() == &arr2d[0]); + } + + { + span s{arr2d}; + CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); + auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr2d}; +#endif + } + + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[11] == 12); + } + + { + span s{arr3d}; + CHECK(s.length() == 0 && s.data() == &arr3d[0][0][0]); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + CHECK(s[0] == 1 && s[5] == 6); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + + { + span s{arr3d[0]}; + CHECK(s.length() == 1 && s.data() == &arr3d[0]); + } + + { + span s{arr3d}; + CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); + auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; + CHECK(workaround_macro()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr3d}; +#endif + } + } + + TEST(from_dynamic_array_constructor) + { + double(*arr)[3][4] = new double[100][3][4]; + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + CHECK_THROW(s[10][3][4], fail_fast); + } + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + span s(arr, 10); + CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); + } + + { + span s(arr, 0); + CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); + } + + delete[] arr; + } + + TEST(from_std_array_constructor) + { + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == 2 && cs.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + + span cs{arr}; + CHECK(cs.size() == 0 && cs.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_const_std_array_constructor) + { + const std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + } + + // TODO This is currently an unsupported scenario. We will come back to it as we revise + // the multidimensional interface and what transformations between dimensionality look like + //{ + // span s{arr}; + // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + //} + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{arr}; +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void) s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); +#endif + } + } + + TEST(from_container_constructor) + { + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + span s{v}; + CHECK(s.size() == narrow_cast(v.size()) && s.data() == v.data()); + + span cs{v}; + CHECK(cs.size() == narrow_cast(v.size()) && cs.data() == v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{str}; + CHECK(s.size() == narrow_cast(str.size()) && s.data() == str.data()); +#endif + span cs{str}; + CHECK(cs.size() == narrow_cast(str.size()) && cs.data() == str.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + span s{cstr}; +#endif + span cs{cstr}; + CHECK(cs.size() == narrow_cast(cstr.size()) && + cs.data() == cstr.data()); + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_vector = []() -> const std::vector { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_vector()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); +#endif + } + + { +#ifdef CONFIRM_COMPILATION_ERRORS + std::map m; + span s{m}; +#endif + } + } + + TEST(from_convertible_span_constructor) + { +#ifdef CONFIRM_COMPILATION_ERRORS + span av1(nullptr, b1); + + auto f = [&]() { span av1(nullptr); }; + CHECK_THROW(f(), fail_fast); +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + span av1 = nullptr; + span av2(av1); + span av2(av1); +#endif + + span avd; +#ifdef CONFIRM_COMPILATION_ERRORS + span avb = avd; +#endif + span avcd = avd; + (void) avcd; + } + + TEST(copy_move_and_assignment) + { + span s1; + CHECK(s1.empty()); + + int arr[] = {3, 4, 5}; + + span s2 = arr; + CHECK(s2.length() == 3 && s2.data() == &arr[0]); + + s2 = s1; + CHECK(s2.empty()); + + auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; + auto use_span = [&](span s) { CHECK(s.length() == 2 && s.data() == &arr[1]); }; + use_span(get_temp_span()); + + s1 = get_temp_span(); + CHECK(s1.length() == 2 && s1.data() == &arr[1]); + } + + template + void fn(const Bounds&) + { + static_assert(Bounds::static_size == 60, "static bounds is wrong size"); + } + TEST(as_span_reshape) + { + int a[3][4][5]; + auto av = as_span(a); + fn(av.bounds()); + auto av2 = as_span(av, dim<60>()); + auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); + auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); + auto av6 = as_span(av5, dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = as_bytes(av6); + + auto av8 = as_span(av7); + + CHECK(av8.size() == av6.size()); + for (auto i = 0; i < av8.size(); i++) { + CHECK(av8[i] == 1); + } + } + + TEST(first) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.first<2>().bounds() == static_bounds<2>())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + span av = arr; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + span av = arr; + CHECK((av.first<5>().bounds() == static_bounds<5>())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds<6>()); + CHECK(av.first<6>().length() == 6); + CHECK(av.first<-1>().length() == -1); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + span av; + CHECK((av.first<0>().bounds() == static_bounds<0>())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(last) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.last<2>().bounds() == static_bounds<2>())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + span av = arr; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + span av = arr; + CHECK((av.last<5>().bounds() == static_bounds<5>())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds<6>())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + span av; + CHECK((av.last<0>().bounds() == static_bounds<0>())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(subspan) + { + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); + CHECK((av.subspan<2, 2>().length() == 2)); + CHECK(av.subspan(2, 2).length() == 2); + CHECK(av.subspan(2, 3).length() == 3); + } + + { + span av = arr; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + } + + { + span av = arr; + CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); + CHECK((av.subspan<0, 5>().length() == 5)); + CHECK(av.subspan(0, 5).length() == 5); + CHECK_THROW(av.subspan(0, 6).length(), fail_fast); + CHECK_THROW(av.subspan(1, 5).length(), fail_fast); + } + + { + span av = arr; + CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<5, 0>().length() == 0)); + CHECK(av.subspan(5, 0).length() == 0); + CHECK_THROW(av.subspan(6, 0).length(), fail_fast); + } + + { + span av; + CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + CHECK((av.subspan<0, 0>().length() == 0)); + CHECK(av.subspan(0, 0).length() == 0); + CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); + } + + { + span av; + CHECK(av.subspan(0).length() == 0); + CHECK_THROW(av.subspan(1).length(), fail_fast); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + + { + span av = arr; + CHECK(av.subspan(0).length() == 5); + CHECK(av.subspan(1).length() == 4); + CHECK(av.subspan(4).length() == 1); + CHECK(av.subspan(5).length() == 0); + CHECK_THROW(av.subspan(6).length(), fail_fast); + auto av2 = av.subspan(1); + for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); + } + } + + TEST(rank) + { + int arr[2] = {1, 2}; + + { + span s; + CHECK(s.rank() == 1); + } + + { + span s = arr; + CHECK(s.rank() == 1); + } + + int arr2d[1][1] = {}; + { + span s = arr2d; + CHECK(s.rank() == 2); + } + } + + TEST(extent) + { + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(s.extent<1>() == 0); +#endif + } + + { + span s; + CHECK(s.extent() == 0); + CHECK(s.extent(0) == 0); + CHECK_THROW(s.extent(1), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 1); + CHECK(s.extent<0>() == 1); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 1); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + + { + int arr2d[1][2] = {}; + + span s = arr2d; + CHECK(s.extent() == 0); + CHECK(s.extent<0>() == 0); + CHECK(s.extent<1>() == 2); + CHECK(s.extent(0) == 0); + CHECK(s.extent(1) == 2); + CHECK_THROW(s.extent(3), fail_fast); + } + } + + TEST(operator_function_call) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s = arr; + CHECK(s(0) == 1); + CHECK_THROW(s(5), fail_fast); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + { + span s = arr2d; + CHECK(s(0, 0) == 1); + CHECK(s(1, 2) == 6); + } + } + + TEST(comparison_operators) + { + { + int arr[10][2]; + auto s1 = as_span(arr); + span s2 = s1; + + CHECK(s1 == s2); + + span s3 = as_span(s1, dim<>(20)); + CHECK(s3 == s2 && s3 == s1); + } + + { + auto s1 = nullptr; + auto s2 = nullptr; + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {2, 1}; // bigger + + span s1 = nullptr; + span s2 = arr; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 == s2); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); + } + + { + int arr[] = {1, 2, 3}; + + span s1 = {&arr[0], 2}; // shorter + span s2 = arr; // longer + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + span s1 = arr1; + span s2 = arr2; + + CHECK(s1 != s2); + CHECK(s2 != s1); + CHECK(!(s1 == s2)); + CHECK(!(s2 == s1)); + CHECK(s1 < s2); + CHECK(!(s2 < s1)); + CHECK(s1 <= s2); + CHECK(!(s2 <= s1)); + CHECK(s2 > s1); + CHECK(!(s1 > s2)); + CHECK(s2 >= s1); + CHECK(!(s1 >= s2)); + } + } + + TEST(basics) + { + auto ptr = as_span(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) { + CHECK(num == 99); + } + + delete[] ptr.data(); + } + + TEST(bounds_checks) + { + int arr[10][2]; + auto av = as_span(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10, 2}]), fail_fast); + } + + void overloaded_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void overloaded_func(span exp, char expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + void fixed_func(span exp, int expected_value) + { + for (auto val : exp) { + CHECK(val == expected_value); + } + } + + TEST(span_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_span(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + + // fixed_func(av, 34); + delete[] data; + } + + TEST(md_access) + { + auto width = 5, height = 20; + + auto imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = + as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (auto i = 0; i < height; i++) { + for (auto j = 0; j < width; j++) { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(as_span) + { + { + int* arr = new int[150]; + + auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + array stdarr{0}; + auto av2 = as_span(stdarr); + overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + (void) t; + auto av3 = as_span(str); + overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + string str; + span strspan = as_span(str); + (void) strspan; + const string cstr; + span cstrspan = as_span(cstr); + (void) cstrspan; + } + + { + int a[3][4][5]; + auto av = as_span(a); + const int(*b)[4][5]; + b = a; + auto bv = as_span(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_span(arr); + (void) cv; + + vector vec(3); + auto dv = as_span(vec); + (void) dv; + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_span(std::move(vec)); +#endif + } + } + + TEST(empty_spans) + { + { + span empty_av(nullptr); + + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + + { + span empty_av = {}; + CHECK(empty_av.bounds().index_bounds() == index<1>{0}); + CHECK_THROW(empty_av[0], fail_fast); + CHECK_THROW(empty_av.begin()[0], fail_fast); + CHECK_THROW(empty_av.cbegin()[0], fail_fast); + for (auto& v : empty_av) { + (void) v; + CHECK(false); + } + } + } + + TEST(index_constructor) + { + auto arr = new int[8]; + for (int i = 0; i < 4; ++i) { + arr[2 * i] = 4 + i; + arr[2 * i + 1] = i; + } + + span av(arr, 8); + + ptrdiff_t a[1] = {0}; + index<1> i = a; + + CHECK(av[i] == 4); + + auto av2 = as_span(av, dim<4>(), dim<>(2)); + ptrdiff_t a2[2] = {0, 1}; + index<2> i2 = a2; + + CHECK(av2[i2] == 0); + CHECK(av2[0][i] == 4); + + delete[] arr; + } + + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = {0, 1, 2}; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // default + index<3> i7; + CHECK(i7[0] == 0); + + // default + index<3> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = {0}; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // default + index<1> i8; + CHECK(i8[0] == 0); + + // default + index<1> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = {0}; + index<3> i4 = {0, 1, 2, 3}; + index<1> i5 = {0, 1}; + } +#endif + } + + TEST(index_operations) + { + ptrdiff_t a[3] = {0, 1, 2}; + ptrdiff_t b[3] = {3, 4, 5}; + index<3> i = a; + index<3> j = b; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + + { + index<3> k = i + j; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 3); + CHECK(k[1] == 5); + CHECK(k[2] == 7); + } + + { + index<3> k = i * 3; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 1); + CHECK(k[1] == 2); + } + } + + void iterate_second_column(span av) + { + auto length = av.size() / 2; + + // view to the second column + auto section = av.section({0, 1}, {length, 1}); + + CHECK(section.size() == length); + for (auto i = 0; i < section.size(); ++i) { + CHECK(section[i][0] == av[i][1]); + } + + for (auto i = 0; i < section.size(); ++i) { + auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + + CHECK(section.bounds().index_bounds()[0] == length); + CHECK(section.bounds().index_bounds()[1] == 1); + for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { + for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { + auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro + CHECK(section[idx] == av[i][1]); + } + } + + size_t check_sum = 0; + for (auto i = 0; i < length; ++i) { + check_sum += av[i][1]; + } + + { + auto idx = 0; + size_t sum = 0; + for (auto num : section) { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); + } + } + + TEST(span_section_iteration) + { + int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; + + // static bounds + { + span av = arr; + iterate_second_column(av); + } + // first bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // second bound is dynamic + { + span av = arr; + iterate_second_column(av); + } + // both bounds are dynamic + { + span av = arr; + iterate_second_column(av); + } + } + + TEST(dynamic_span_section_iteration) + { + auto height = 4, 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(arr, size); + + // first bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // second bound is dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + // both bounds are dynamic + { + span av2 = as_span(av, dim<>(height), dim<>(width)); + iterate_second_column(av2); + } + + delete[] arr; + } + + TEST(span_structure_size) + { + double(*arr)[3][4] = new double[100][3][4]; + span av1(arr, 10); + + struct EffectiveStructure + { + double* v1; + ptrdiff_t v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + (void) av2; + } + + TEST(fixed_size_conversions) + { + int arr[] = {1, 2, 3, 4}; + + // converting to an span from an equal size array is ok + span av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + span av = av4; + (void) av; + } + { + span av = arr; + (void) av; + } + +// initialization or assignment to static span that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av2 = arr; + } + { + span av2 = av4; + } +#endif + + { + span av = arr; + span av2 = av; + (void) av2; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av = arr; + span av2 = av.as_span(dim<2>(), dim<2>()); + } +#endif + + { + span av = arr; + span av2 = as_span(av, dim<>(2), dim<>(2)); + auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; + CHECK(workaround_macro()); + } + + // but doing so explicitly is ok + + // you can convert statically + { + span av2 = {arr, 2}; + (void) av2; + } + { + span av2 = av4.first<1>(); + (void) av2; + } + + // ...or dynamically + { + // NB: implicit conversion to span from span + span av2 = av4.first(1); + (void) av2; + } + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + span av4 = arr2; + } + { + span av2 = arr2; + span av4 = av2; + } +#endif + { + auto f = [&]() { + span av9 = {arr2, 2}; + (void) av9; + }; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + span av = arr2; + auto f = [&]() { + span av2 = av; + (void) av2; + }; + CHECK_THROW(f(), fail_fast); + } + + TEST(as_writeable_bytes) + { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + span av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + span av; + auto wav = as_writeable_bytes(av); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.size_bytes() == 0); + } + + { + span av = a; + auto wav = as_writeable_bytes(av); + CHECK(wav.data() == (byte*) &a[0]); + CHECK(wav.length() == sizeof(a)); + } + } + + TEST(iterator) + { + int a[] = {1, 2, 3, 4}; + + { + span av = a; + auto wav = as_writeable_bytes(av); + for (auto& b : wav) { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 0); + } + } + + { + span av = a; + for (auto& n : av) { + n = 1; + } + for (size_t i = 0; i < 4; ++i) { + CHECK(a[i] == 1); + } + } + } +#endif +} + +int main(int, const char* []) { return UnitTest::RunAllTests(); } -- cgit v1.2.3 From d3929c59a07a0bd1137601cf2866ffd6dd68d795 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 24 Feb 2016 16:11:33 -0800 Subject: Began reimplementation of span. Basic constructors. --- include/gsl_util.h | 1 + include/span.h | 132 +++++++++++++++++++++++++++++++++++++++++++++++---- tests/span_tests.cpp | 2 +- 3 files changed, 126 insertions(+), 9 deletions(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index 316f2a1..508672c 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -106,6 +106,7 @@ inline T narrow(U u) T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); +#pragma warning(suppress:4127) // suppress warning from MSVC compiler about constant in if-test if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) throw narrowing_error(); return t; diff --git a/include/span.h b/include/span.h index 7df4964..b8c1196 100644 --- a/include/span.h +++ b/include/span.h @@ -21,16 +21,9 @@ #include "gsl_assert.h" #include "gsl_util.h" -#include #include -#include -#include -#include -#include -#include #include -#include -#include +#include #include #include #include @@ -79,6 +72,129 @@ namespace gsl { +// [views.constants], constants +constexpr const std::ptrdiff_t dynamic_extent = -1; + + +// [span], class template span +template +class span { +public: + // constants and types + using element_type = ElementType; + using index_type = std::ptrdiff_t; + using pointer = element_type*; + using reference = element_type&; +#if 0 // TODO + using iterator = /*implementation-defined */; + using const_iterator = /* implementation-defined */; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; +#endif + constexpr static const index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : data_(nullptr), size_(0) + { static_assert(extent == dynamic_extent || extent == 0, "Cannot default initialize a fixed-length span."); } + constexpr span(nullptr_t) noexcept : span() {} + constexpr span(pointer ptr, index_type count) : data_(ptr), size_(count) + { Expects((!ptr && count == 0) || (ptr && count >= 0)); } +#if 0 // TODO + constexpr span(pointer firstElem, pointer lastElem); + template + constexpr span(element_type(&arr)[N]); + template + constexpr span(array, N>& arr); + template + constexpr span(const array, N>& arr); + template + constexpr span(Container& cont); + template + span(const Container&&) = delete; + constexpr span(const span& other) noexcept = default; + constexpr span(span&& other) noexcept = default; + template + constexpr span(const span& other); + template + constexpr span(span&& other); + ~span() noexcept = default; + constexpr span& operator=(const span& other) noexcept = default; + constexpr span& operator=(span&& other) noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const; + template + constexpr span last() const; + template + constexpr span subspan() const; + constexpr span first(index_type count) const; + constexpr span last(index_type count) const; + constexpr span subspan(index_type offset, index_type count = dynamic_extent) const; +#endif + // [span.obs], span observers + constexpr index_type length() const noexcept { return size(); } + constexpr index_type size() const noexcept { return size_; } + constexpr index_type length_bytes() const noexcept { return size_bytes(); } + constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } + constexpr bool empty() const noexcept { return size() == 0; } + +#if 0 // TODO + // [span.elem], span element access + constexpr reference operator[](index_type idx) const; + constexpr reference operator()(index_type idx) const; +#endif + constexpr pointer data() const noexcept { return data_; } +#if 0 // TODO + // [span.iter], span iterator support + iterator begin() const noexcept; + iterator end() const noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + reverse_iterator rbegin() const noexcept; + reverse_iterator rend() const noexcept; + + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; +#endif +private: + pointer data_; + index_type size_; +}; + + +#if 0 // TODO +// [span.comparison], span comparison operators +template +constexpr bool operator==(const span& l, const span& r) const noexcept; + +template +constexpr bool operator!=(const span& l, const span& r) const noexcept; + +template +constexpr bool operator<(const span& l, const span& r) const noexcept; + +template +constexpr bool operator<=(const span& l, const span& r) const noexcept; + +template +constexpr bool operator>(const span& l, const span& r) const noexcept; + +template +constexpr bool operator>=(const span& l, const span& r) const noexcept; +#endif + + +#if 0 // TODO +// [span.objectrep], views of object representation +template +constexpr span as_bytes(span s) noexcept; + +template +constexpr span as_writeable_bytes(span) noexcept; +#endif } // namespace gsl diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 83eec0e..233ab00 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -39,7 +39,6 @@ struct DerivedClass : BaseClass SUITE(span_tests) { -#if 0 TEST(default_constructor) { { @@ -116,6 +115,7 @@ SUITE(span_tests) } } +#if 0 TEST(from_nullptr_length_constructor) { { -- cgit v1.2.3 From cc22f2bf4263042d5294c7b97a443d9191c2eb1f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 25 Feb 2016 11:42:26 -0800 Subject: first/last constructor working. --- include/span.h | 27 ++++++++++----- tests/span_tests.cpp | 94 ++++++++++++++-------------------------------------- 2 files changed, 43 insertions(+), 78 deletions(-) diff --git a/include/span.h b/include/span.h index b8c1196..38cf9f5 100644 --- a/include/span.h +++ b/include/span.h @@ -96,13 +96,20 @@ public: // [span.cons], span constructors, copy, assignment, and destructor constexpr span() noexcept : data_(nullptr), size_(0) { static_assert(extent == dynamic_extent || extent == 0, "Cannot default initialize a fixed-length span."); } - constexpr span(nullptr_t) noexcept : span() {} + + constexpr span(nullptr_t) noexcept : span() + {} + constexpr span(pointer ptr, index_type count) : data_(ptr), size_(count) - { Expects((!ptr && count == 0) || (ptr && count >= 0)); } -#if 0 // TODO - constexpr span(pointer firstElem, pointer lastElem); + { Expects(((!ptr && count == 0) || (ptr && count >= 0)) && (extent == dynamic_extent || extent == count)); } + + constexpr span(pointer firstElem, pointer lastElem) : data_(firstElem), size_(std::distance(firstElem, lastElem)) + { Expects(size_ >= 0 && (extent == dynamic_extent || extent == size_)); } + template - constexpr span(element_type(&arr)[N]); + constexpr span(element_type(&arr)[N]) {} + +#if 0 // TODO template constexpr span(array, N>& arr); template @@ -139,11 +146,13 @@ public: constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } -#if 0 // TODO // [span.elem], span element access - constexpr reference operator[](index_type idx) const; - constexpr reference operator()(index_type idx) const; -#endif + constexpr reference operator[](index_type idx) const + { + Expects(idx >= 0 && idx < size_); + return data_[idx]; + } + constexpr reference operator()(index_type idx) const { return this->operator[](idx); } constexpr pointer data() const noexcept { return data_; } #if 0 // TODO // [span.iter], span iterator support diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 233ab00..11004ae 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -115,7 +115,6 @@ SUITE(span_tests) } } -#if 0 TEST(from_nullptr_length_constructor) { { @@ -135,10 +134,8 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{nullptr, 0}; - CHECK(s.length() == 1 && s.data() == nullptr); // explains why it can't compile -#endif + auto workaround_macro = []() { span s{ nullptr, 0 }; }; + CHECK_THROW(workaround_macro(), fail_fast); } { @@ -166,56 +163,6 @@ SUITE(span_tests) } } - TEST(from_element_constructor) - { - int i = 5; - - { - span s = i; - CHECK(s.length() == 1 && s.data() == &i); - CHECK(s[0] == 5); - - span cs = i; - CHECK(cs.length() == 1 && cs.data() == &i); - CHECK(cs[0] == 5); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - const j = 1; - span s = j; -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s = i; - CHECK(s.length() == 0 && s.data() == &i); -#endif - } - - { - span s = i; - CHECK(s.length() == 1 && s.data() == &i); - CHECK(s[0] == 5); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s = i; - CHECK(s.length() == 2 && s.data() == &i); -#endif - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - auto get_a_temp = []() -> int { return 4; }; - auto use_a_span = [](span s) { (void) s; }; - use_a_span(get_a_temp()); -#endif - } - } - TEST(from_pointer_length_constructor) { int arr[4] = {1, 2, 3, 4}; @@ -271,30 +218,39 @@ SUITE(span_tests) CHECK(s.length() == 0 && s.data() == &arr[0]); } - { - auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} - { - int* p = nullptr; - auto workaround_macro = [&]() { span s{&arr[0], p}; }; - CHECK_THROW(workaround_macro(), fail_fast); - } + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { span s{&arr[0], p}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} { int* p = nullptr; - auto workaround_macro = [&]() { span s{p, p}; }; - CHECK_THROW(workaround_macro(), fail_fast); + span s{ p, p }; + CHECK(s.length() == 0 && s.data() == nullptr); } { int* p = nullptr; - auto workaround_macro = [&]() { span s{&arr[0], p}; }; - CHECK_THROW(workaround_macro(), fail_fast); + span s{ p, p }; + CHECK(s.length() == 0 && s.data() == nullptr); } - } + // this will fail the std::distance() precondition, which asserts on MSVC debug builds + //{ + // int* p = nullptr; + // auto workaround_macro = [&]() { span s{&arr[0], p}; }; + // CHECK_THROW(workaround_macro(), fail_fast); + //} + } +#if 0 TEST(from_array_constructor) { int arr[5] = {1, 2, 3, 4, 5}; -- cgit v1.2.3 From 502cd6650aed90cfe221857dfc3547fafc54d6f8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 28 Feb 2016 00:50:53 -0800 Subject: Checking in to continue working elsewhere. --- include/span.h | 90 ++++++++++++++++++++++++++++++++++++++++++++-------- tests/span_tests.cpp | 81 ++++++++++++++-------------------------------- 2 files changed, 100 insertions(+), 71 deletions(-) diff --git a/include/span.h b/include/span.h index 38cf9f5..4983295 100644 --- a/include/span.h +++ b/include/span.h @@ -94,20 +94,22 @@ public: constexpr static const index_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : data_(nullptr), size_(0) - { static_assert(extent == dynamic_extent || extent == 0, "Cannot default initialize a fixed-length span."); } + constexpr span() noexcept : storage_(nullptr, extent_type<0>()) + {} constexpr span(nullptr_t) noexcept : span() {} - constexpr span(pointer ptr, index_type count) : data_(ptr), size_(count) - { Expects(((!ptr && count == 0) || (ptr && count >= 0)) && (extent == dynamic_extent || extent == count)); } + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) + { Expects(((!ptr && count == 0) || (ptr && count >= 0))); } - constexpr span(pointer firstElem, pointer lastElem) : data_(firstElem), size_(std::distance(firstElem, lastElem)) - { Expects(size_ >= 0 && (extent == dynamic_extent || extent == size_)); } + constexpr span(pointer firstElem, pointer lastElem) + : storage_(firstElem, std::distance(firstElem, lastElem)) + {} template - constexpr span(element_type(&arr)[N]) {} + constexpr span(element_type(&arr)[N]) : storage_(&arr[0], extent_type()) + {} #if 0 // TODO template @@ -141,7 +143,7 @@ public: #endif // [span.obs], span observers constexpr index_type length() const noexcept { return size(); } - constexpr index_type size() const noexcept { return size_; } + constexpr index_type size() const noexcept { return storage_.size(); } constexpr index_type length_bytes() const noexcept { return size_bytes(); } constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } @@ -149,11 +151,11 @@ public: // [span.elem], span element access constexpr reference operator[](index_type idx) const { - Expects(idx >= 0 && idx < size_); - return data_[idx]; + Expects(idx >= 0 && idx < storage_.size()); + return storage_.data()[idx]; } constexpr reference operator()(index_type idx) const { return this->operator[](idx); } - constexpr pointer data() const noexcept { return data_; } + constexpr pointer data() const noexcept { return storage_.data(); } #if 0 // TODO // [span.iter], span iterator support iterator begin() const noexcept; @@ -169,8 +171,70 @@ public: const_reverse_iterator crend() const noexcept; #endif private: - pointer data_; - index_type size_; + template + class extent_type; + + template + class extent_type + { + public: + static_assert(Extent >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type) noexcept + { + static_assert(Other == Extent, + "Mismatch between fixed-size extent and size of initializing data."); + } + + constexpr extent_type(index_type size) + { Expects(size == Extent); } + + constexpr inline index_type size() const noexcept { return Extent; } + }; + + template <> + class extent_type + { + public: + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + {} + + explicit constexpr extent_type(index_type size) : size_(size) + { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept + { return size_; } + + private: + index_type size_; + }; + + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + template + storage_type(pointer data, OtherExtentType ext) + : ExtentType(ext), data_(data) {} + + //storage_type(pointer data, ExtentType ext) + // : ExtentType(ext), data_(data) {} + + constexpr inline pointer data() const noexcept + { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; }; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 11004ae..e4bdef4 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -73,6 +73,19 @@ SUITE(span_tests) } } + TEST(size_optimization) + { + { + span s; + CHECK(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t)); + } + + { + span s; + CHECK(sizeof(s) == sizeof(int*)); + } + } + TEST(from_nullptr_constructor) { { @@ -250,7 +263,7 @@ SUITE(span_tests) // CHECK_THROW(workaround_macro(), fail_fast); //} } -#if 0 + TEST(from_array_constructor) { int arr[5] = {1, 2, 3, 4, 5}; @@ -265,10 +278,11 @@ SUITE(span_tests) CHECK(s.length() == 5 && s.data() == &arr[0]); } - { + int arr2d[2][3] = { 1, 2, 3, 4, 5, 6 }; + #ifdef CONFIRM_COMPILATION_ERRORS + { span s{arr}; -#endif } { @@ -276,8 +290,6 @@ SUITE(span_tests) CHECK(s.length() == 0 && s.data() == &arr[0]); } - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - { span s{arr2d}; CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); @@ -290,43 +302,17 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; -#endif + span s{ arr2d }; } - - { - span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - CHECK(s[0] == 1 && s[5] == 6); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; #endif - } - { - span s{arr2d[0]}; + span s{ arr2d[0] }; CHECK(s.length() == 1 && s.data() == &arr2d[0]); } - { - span s{arr2d}; - CHECK(s.length() == 6 && s.data() == &arr2d[0][0]); - auto workaround_macro = [&]() { return s[{1, 2}] == 6; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr2d}; -#endif - } - int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; +#ifdef CONFIRM_COMPILATION_ERRORS { span s{arr3d}; CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); @@ -339,9 +325,7 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS span s{arr3d}; -#endif } { @@ -349,32 +333,13 @@ SUITE(span_tests) CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); CHECK(s[0] == 1 && s[5] == 6); } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; #endif - } - - { - span s{arr3d[0]}; - CHECK(s.length() == 1 && s.data() == &arr3d[0]); - } - { - span s{arr3d}; - CHECK(s.length() == 12 && s.data() == &arr3d[0][0][0]); - auto workaround_macro = [&]() { return s[{2, 1, 0}] == 11; }; - CHECK(workaround_macro()); - } - - { -#ifdef CONFIRM_COMPILATION_ERRORS - span s{arr3d}; -#endif + //span s{arr3d[0]}; + //CHECK(s.length() == 1 && s.data() == &arr3d[0]); } } - +#if 0 TEST(from_dynamic_array_constructor) { double(*arr)[3][4] = new double[100][3][4]; -- cgit v1.2.3 From f61a9bba48fa410e2df4519793f7480be4b85934 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 29 Feb 2016 13:16:48 -0800 Subject: Added array constructors. --- include/span.h | 15 ++++++++++---- tests/span_tests.cpp | 57 ++++++++++------------------------------------------ 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/include/span.h b/include/span.h index 4983295..2d95899 100644 --- a/include/span.h +++ b/include/span.h @@ -108,14 +108,21 @@ public: {} template - constexpr span(element_type(&arr)[N]) : storage_(&arr[0], extent_type()) + constexpr span(element_type(&arr)[N]) + : storage_(&arr[0], extent_type()) {} -#if 0 // TODO template - constexpr span(array, N>& arr); + constexpr span(std::array, N>& arr) + : storage_(&arr[0], extent_type()) + {} + template - constexpr span(const array, N>& arr); + constexpr span(const std::array, N>& arr) + : storage_(&arr[0], extent_type()) + {} + +#if 0 // TODO template constexpr span(Container& cont); template diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index e4bdef4..e13694c 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -306,7 +306,7 @@ SUITE(span_tests) } #endif { - span s{ arr2d[0] }; + span s{ &(arr2d[0]), 1 }; CHECK(s.length() == 1 && s.data() == &arr2d[0]); } @@ -335,34 +335,18 @@ SUITE(span_tests) } #endif { - //span s{arr3d[0]}; - //CHECK(s.length() == 1 && s.data() == &arr3d[0]); + span s{&arr3d[0], 1}; + CHECK(s.length() == 1 && s.data() == &arr3d[0]); } } -#if 0 + TEST(from_dynamic_array_constructor) { double(*arr)[3][4] = new double[100][3][4]; { - span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - CHECK_THROW(s[10][3][4], fail_fast); - } - - { - span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - } - - { - span s(arr, 10); - CHECK(s.length() == 120 && s.data() == &arr[0][0][0]); - } - - { - span s(arr, 0); - CHECK(s.length() == 0 && s.data() == &arr[0][0][0]); + span s(&arr[0][0][0], 10); + CHECK(s.length() == 10 && s.data() == &arr[0][0][0]); } delete[] arr; @@ -388,6 +372,7 @@ SUITE(span_tests) CHECK(cs.size() == narrow_cast(arr.size()) && cs.data() == arr.data()); } +#ifdef CONFIRM_COMPILATION_ERRORS { span s{arr}; CHECK(s.size() == 2 && s.data() == arr.data()); @@ -404,27 +389,17 @@ SUITE(span_tests) CHECK(cs.size() == 0 && cs.data() == arr.data()); } - // TODO This is currently an unsupported scenario. We will come back to it as we revise - // the multidimensional interface and what transformations between dimensionality look like - //{ - // span s{arr}; - // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - //} - { -#ifdef CONFIRM_COMPILATION_ERRORS span s{arr}; -#endif } { -#ifdef CONFIRM_COMPILATION_ERRORS auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; auto take_a_span = [](span s) { (void) s; }; // try to take a temporary std::array take_a_span(get_an_array()); -#endif } +#endif } TEST(from_const_std_array_constructor) @@ -440,7 +415,7 @@ SUITE(span_tests) span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } - +#ifdef CONFIRM_COMPILATION_ERRORS { span s{arr}; CHECK(s.size() == 2 && s.data() == arr.data()); @@ -451,29 +426,19 @@ SUITE(span_tests) CHECK(s.size() == 0 && s.data() == arr.data()); } - // TODO This is currently an unsupported scenario. We will come back to it as we revise - // the multidimensional interface and what transformations between dimensionality look like - //{ - // span s{arr}; - // CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); - //} - { -#ifdef CONFIRM_COMPILATION_ERRORS span s{arr}; -#endif } { -#ifdef CONFIRM_COMPILATION_ERRORS auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; auto take_a_span = [](span s) { (void) s; }; // try to take a temporary std::array take_a_span(get_an_array()); -#endif } +#endif } - +#if 0 TEST(from_container_constructor) { std::vector v = {1, 2, 3}; -- cgit v1.2.3 From c40094a532f932bc0ed7af2279161dc4bc72b85d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 1 Mar 2016 12:11:41 -0800 Subject: Added from-container constructors. --- include/multi_span.h | 18 ++++++++-------- include/span.h | 50 +++++++++++++++++++++++++++++++++++++-------- include/string_span.h | 28 ++++++++++++------------- tests/span_tests.cpp | 4 ++-- tests/string_span_tests.cpp | 2 +- 5 files changed, 68 insertions(+), 34 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index b070f29..5eb9109 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1156,22 +1156,22 @@ namespace details }; template - struct is_span_oracle : std::false_type + struct is_multi_span_oracle : std::false_type { }; template - struct is_span_oracle> : std::true_type + struct is_multi_span_oracle> : std::true_type { }; template - struct is_span_oracle> : std::true_type + struct is_multi_span_oracle> : std::true_type { }; template - struct is_span : is_span_oracle> + struct is_multi_span : is_multi_span_oracle> { }; } @@ -1324,7 +1324,7 @@ public: // type-requirements: container must have .size(), operator[] which are value_type compatible template ::value && + !details::is_multi_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, @@ -1338,7 +1338,7 @@ public: // prevent constructing from temporary containers template ::value && + !details::is_multi_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, @@ -1595,7 +1595,7 @@ template as_span(SpanType s, Dimensions2... dims) { - static_assert(details::is_span::value, + static_assert(details::is_multi_span::value, "Variadic as_span() is for reshaping existing spans."); using BoundsType = typename multi_span::bounds_type; @@ -1718,7 +1718,7 @@ constexpr multi_span as_span(T* begin, T* end) template constexpr auto as_span(Cont& arr) -> std::enable_if_t< - !details::is_span>::value, + !details::is_multi_span>::value, multi_span, dynamic_range>> { Expects(arr.size() < PTRDIFF_MAX); @@ -1727,7 +1727,7 @@ constexpr auto as_span(Cont& arr) -> std::enable_if_t< template constexpr auto as_span(Cont&& arr) -> std::enable_if_t< - !details::is_span>::value, + !details::is_multi_span>::value, multi_span, dynamic_range>> = delete; // from basic_string which doesn't have nonconst .data() member like other contiguous containers diff --git a/include/span.h b/include/span.h index 2d95899..eab2659 100644 --- a/include/span.h +++ b/include/span.h @@ -72,12 +72,35 @@ namespace gsl { +template +class span; + + +namespace details +{ +template +struct is_span_oracle : std::false_type +{ +}; + +template +struct is_span_oracle> : std::true_type +{ +}; + +template +struct is_span : is_span_oracle> +{ +}; +} // namespace details + + // [views.constants], constants constexpr const std::ptrdiff_t dynamic_extent = -1; // [span], class template span -template +template class span { public: // constants and types @@ -122,11 +145,25 @@ public: : storage_(&arr[0], extent_type()) {} -#if 0 // TODO - template - constexpr span(Container& cont); - template + // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement + // on Container to be a contiguous sequence container. + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value> + > + constexpr span(Container& cont) : span(cont.data(), cont.size()) {} + + // NB: the SFINAE here uses .data() as an incomplete/imperfect proxy for the requirement + // on Container to be a contiguous sequence container. + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value> + > span(const Container&&) = delete; + +#if 0 // TODO constexpr span(const span& other) noexcept = default; constexpr span(span&& other) noexcept = default; template @@ -231,9 +268,6 @@ private: storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) {} - //storage_type(pointer data, ExtentType ext) - // : ExtentType(ext), data_(data) {} - constexpr inline pointer data() const noexcept { return data_; } diff --git a/include/string_span.h b/include/string_span.h index c58cef9..69c3a81 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -297,7 +297,7 @@ public: // from containers. Containers must have .size() and .data() function signatures template ::value + typename Dummy = std::enable_if_t::value && !details::is_basic_string_span::value && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span && std::is_convertible::value @@ -309,7 +309,7 @@ public: // disallow creation from temporary containers and strings template ::value + typename Dummy = std::enable_if_t::value && !details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -640,7 +640,7 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -654,7 +654,7 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -694,7 +694,7 @@ bool operator!=(const T& one, gsl::basic_string_span other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -707,7 +707,7 @@ bool operator!=(gsl::basic_string_span one, const T& other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -748,7 +748,7 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -762,7 +762,7 @@ bool operator<(gsl::basic_string_span one, const T& other) noexce template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -802,7 +802,7 @@ bool operator<=(const T& one, gsl::basic_string_span other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -815,7 +815,7 @@ bool operator<=(gsl::basic_string_span one, const T& other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -854,7 +854,7 @@ bool operator>(const T& one, gsl::basic_string_span other) noexce template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -867,7 +867,7 @@ bool operator>(gsl::basic_string_span one, const T& other) noexce template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -906,7 +906,7 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -919,7 +919,7 @@ bool operator>=(gsl::basic_string_span one, const T& other) noexc template ::value + !gsl::details::is_multi_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index e13694c..e68493c 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -438,7 +438,7 @@ SUITE(span_tests) } #endif } -#if 0 + TEST(from_container_constructor) { std::vector v = {1, 2, 3}; @@ -512,7 +512,7 @@ SUITE(span_tests) #endif } } - +#if 0 TEST(from_convertible_span_constructor) { #ifdef CONFIRM_COMPILATION_ERRORS diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index cd301a0..6876253 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -46,7 +46,7 @@ SUITE(string_span_tests) TEST(TestConstructFromStdVector) { std::vector vec(5, 'h'); - string_span<> v = vec; + string_span<> v {vec}; CHECK(v.length() == static_cast::size_type>(vec.size())); } -- cgit v1.2.3 From 717a2e35f13e723c491aa1d90364a7831cdbb526 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 16 Mar 2016 19:39:55 -0700 Subject: Added conversion ctors. --- include/span.h | 73 ++++++++++++++++++++++++++++++++++++++++++++++++---- tests/span_tests.cpp | 46 ++++++++++++++++++++------------- 2 files changed, 96 insertions(+), 23 deletions(-) diff --git a/include/span.h b/include/span.h index eab2659..3c5b53d 100644 --- a/include/span.h +++ b/include/span.h @@ -92,6 +92,53 @@ template struct is_span : is_span_oracle> { }; + +template +struct is_allowed_pointer_conversion + : std::integral_constant::value && + std::is_pointer::value && + std::is_convertible::value + > +{ +}; + +template +struct is_allowed_integral_conversion + : std::integral_constant::value && + std::is_integral::value && + sizeof(From) == sizeof(To) && + alignof(From) == alignof(To) && + std::is_convertible::value + > +{ +}; + +template +struct is_allowed_element_type_conversion + : std::integral_constant>::value || + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value + > +{ +}; + +template +struct is_allowed_element_type_conversion + : std::integral_constant::value> +{ +}; + +template +struct is_allowed_element_type_conversion + : std::integral_constant +{ +}; + + + } // namespace details @@ -163,13 +210,29 @@ public: > span(const Container&&) = delete; -#if 0 // TODO constexpr span(const span& other) noexcept = default; constexpr span(span&& other) noexcept = default; - template - constexpr span(const span& other); - template - constexpr span(span&& other); + + template ::value && + details::is_allowed_element_type_conversion::value + > + > + constexpr span(const span& other) + : storage_(reinterpret_cast(other.data()), other.length()) + {} + + template ::value && + details::is_allowed_element_type_conversion::value + > + > + constexpr span(span&& other) + : storage_(reinterpret_cast(other.data()), other.length()) + { + } + +#if 0 // TODO ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; constexpr span& operator=(span&& other) noexcept = default; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index e68493c..0b65507 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -512,34 +512,44 @@ SUITE(span_tests) #endif } } -#if 0 + TEST(from_convertible_span_constructor) { -#ifdef CONFIRM_COMPILATION_ERRORS - span av1(nullptr, b1); + { + span avd; + span avcd = avd; + (void)avcd; + } - auto f = [&]() { span av1(nullptr); }; - CHECK_THROW(f(), fail_fast); + { +#ifdef CONFIRM_COMPILATION_ERRORS + span avd; + span avb = avd; + (void) avb; #endif + } -#ifdef CONFIRM_COMPILATION_ERRORS - static_bounds b12(b11); - b12 = b11; - b11 = b12; + { + span s; + span s2 = s; + (void)s2; + } - span av1 = nullptr; - span av2(av1); - span av2(av1); -#endif + { + span s; + span s2 = s; + (void)s2; + } - span avd; + { #ifdef CONFIRM_COMPILATION_ERRORS - span avb = avd; + span s; + span s2 = s; + (void)s2; #endif - span avcd = avd; - (void) avcd; + } } - +#if 0 TEST(copy_move_and_assignment) { span s1; -- cgit v1.2.3 From 3d4c34966a093e71f5f6c47ec68d06f2700fd7e2 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 17 Mar 2016 17:20:33 -0700 Subject: Fixed ctors for Container and std::array to accept temporaries. --- include/multi_span.h | 2 +- include/span.h | 13 ++++++------ tests/span_tests.cpp | 57 ++++++++++++++++++++++------------------------------ 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index 5eb9109..523f324 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -2159,8 +2159,8 @@ public: value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; - ; } + bool operator==(const general_span_iterator& rhs) const noexcept { Expects(m_container == rhs.m_container); diff --git a/include/span.h b/include/span.h index 3c5b53d..7e8c119 100644 --- a/include/span.h +++ b/include/span.h @@ -187,7 +187,7 @@ public: : storage_(&arr[0], extent_type()) {} - template + template ::value>> constexpr span(const std::array, N>& arr) : storage_(&arr[0], extent_type()) {} @@ -201,15 +201,14 @@ public: > constexpr span(Container& cont) : span(cont.data(), cont.size()) {} - // NB: the SFINAE here uses .data() as an incomplete/imperfect proxy for the requirement - // on Container to be a contiguous sequence container. template ::value && + class = std::enable_if_t::value && + !details::is_span::value && std::is_convertible::value && std::is_convertible().data())>::value> > - span(const Container&&) = delete; - + constexpr span(const Container& cont) : span(cont.data(), cont.size()) {} + constexpr span(const span& other) noexcept = default; constexpr span(span&& other) noexcept = default; @@ -232,10 +231,10 @@ public: { } -#if 0 // TODO ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; constexpr span& operator=(span&& other) noexcept = default; +#if 0 // TODO // [span.sub], span subviews template diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 0b65507..8534b3e 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -394,12 +394,18 @@ SUITE(span_tests) } { - auto get_an_array = []() { return std::array{1, 2, 3, 4}; }; - auto take_a_span = [](span s) { (void) s; }; + auto get_an_array = []()->std::array { return{1, 2, 3, 4}; }; + auto take_a_span = [](span s) { (void)s; }; // try to take a temporary std::array take_a_span(get_an_array()); } #endif + { + auto get_an_array = []() -> std::array { return { 1, 2, 3, 4 }; }; + auto take_a_span = [](span s) { (void)s; }; + // try to take a temporary std::array + take_a_span(get_an_array()); + } } TEST(from_const_std_array_constructor) @@ -481,14 +487,26 @@ SUITE(span_tests) #endif } + { + auto get_temp_vector = []() -> std::vector { return{}; }; + auto use_span = [](span s) { (void)s; }; + use_span(get_temp_vector()); + } + { #ifdef CONFIRM_COMPILATION_ERRORS - auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto get_temp_string = []() -> std::string { return{}; }; + auto use_span = [](span s) { (void)s; }; use_span(get_temp_string()); #endif } + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span s) { (void) s; }; + use_span(get_temp_string()); + } + { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_vector = []() -> const std::vector { return {}; }; @@ -549,7 +567,7 @@ SUITE(span_tests) #endif } } -#if 0 + TEST(copy_move_and_assignment) { span s1; @@ -570,34 +588,7 @@ SUITE(span_tests) s1 = get_temp_span(); CHECK(s1.length() == 2 && s1.data() == &arr[1]); } - - template - void fn(const Bounds&) - { - static_assert(Bounds::static_size == 60, "static bounds is wrong size"); - } - TEST(as_span_reshape) - { - int a[3][4][5]; - auto av = as_span(a); - fn(av.bounds()); - auto av2 = as_span(av, dim<60>()); - auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); - auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); - auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); - auto av6 = as_span(av5, dim<12>(), dim<>(5)); - - fill(av6.begin(), av6.end(), 1); - - auto av7 = as_bytes(av6); - - auto av8 = as_span(av7); - - CHECK(av8.size() == av6.size()); - for (auto i = 0; i < av8.size(); i++) { - CHECK(av8[i] == 1); - } - } +#if 0 TEST(first) { -- cgit v1.2.3 From c8a412f0280d0fd79e5f1e150dafcf30b2751cbe Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 18 Mar 2016 16:49:29 -0700 Subject: Implemented first, last, subspan. --- include/span.h | 47 ++++++++++++++++++++++----- tests/span_tests.cpp | 92 ++++------------------------------------------------ 2 files changed, 45 insertions(+), 94 deletions(-) diff --git a/include/span.h b/include/span.h index 7e8c119..032779c 100644 --- a/include/span.h +++ b/include/span.h @@ -234,19 +234,50 @@ public: ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; constexpr span& operator=(span&& other) noexcept = default; -#if 0 // TODO // [span.sub], span subviews template - constexpr span first() const; + constexpr span first() const + { + Expects(Count >= 0 && Count <= size()); + return { data(), Count }; + } + template - constexpr span last() const; + constexpr span last() const + { + Expects(Count >= 0 && Count <= size()); + return{ Count == 0 ? data() : data() + (size() - Count), Count }; + } + template - constexpr span subspan() const; - constexpr span first(index_type count) const; - constexpr span last(index_type count) const; - constexpr span subspan(index_type offset, index_type count = dynamic_extent) const; -#endif + constexpr span subspan() const + { + Expects((Offset == 0 || Offset > 0 && Offset <= size()) && + (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())); + return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count }; + } + + constexpr span first(index_type count) const + { + Expects(count >= 0 && count <= size()); + return { data(), count }; + } + + constexpr span last(index_type count) const + { + Expects(count >= 0 && count <= size()); + return { count == 0 ? data() : data() + (size() - count), count }; + } + + constexpr span subspan(index_type offset, + index_type count = dynamic_extent) const + { + Expects((offset == 0 || offset > 0 && offset <= size()) && + (count == dynamic_extent || count >= 0 && offset + count <= size())); + return { data() + offset, count == dynamic_extent ? size() - offset : count }; + } + // [span.obs], span observers constexpr index_type length() const noexcept { return size(); } constexpr index_type size() const noexcept { return storage_.size(); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8534b3e..01784f6 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -588,7 +588,6 @@ SUITE(span_tests) s1 = get_temp_span(); CHECK(s1.length() == 2 && s1.data() == &arr[1]); } -#if 0 TEST(first) { @@ -596,21 +595,18 @@ SUITE(span_tests) { span av = arr; - CHECK((av.first<2>().bounds() == static_bounds<2>())); CHECK(av.first<2>().length() == 2); CHECK(av.first(2).length() == 2); } { span av = arr; - CHECK((av.first<0>().bounds() == static_bounds<0>())); CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } { span av = arr; - CHECK((av.first<5>().bounds() == static_bounds<5>())); CHECK(av.first<5>().length() == 5); CHECK(av.first(5).length() == 5); } @@ -618,7 +614,6 @@ SUITE(span_tests) { span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - CHECK(av.first<6>().bounds() == static_bounds<6>()); CHECK(av.first<6>().length() == 6); CHECK(av.first<-1>().length() == -1); #endif @@ -626,8 +621,7 @@ SUITE(span_tests) } { - span av; - CHECK((av.first<0>().bounds() == static_bounds<0>())); + span av; CHECK(av.first<0>().length() == 0); CHECK(av.first(0).length() == 0); } @@ -639,21 +633,18 @@ SUITE(span_tests) { span av = arr; - CHECK((av.last<2>().bounds() == static_bounds<2>())); CHECK(av.last<2>().length() == 2); CHECK(av.last(2).length() == 2); } { span av = arr; - CHECK((av.last<0>().bounds() == static_bounds<0>())); CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } { span av = arr; - CHECK((av.last<5>().bounds() == static_bounds<5>())); CHECK(av.last<5>().length() == 5); CHECK(av.last(5).length() == 5); } @@ -661,15 +652,13 @@ SUITE(span_tests) { span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS - CHECK((av.last<6>().bounds() == static_bounds<6>())); CHECK(av.last<6>().length() == 6); #endif CHECK_THROW(av.last(6).length(), fail_fast); } { - span av; - CHECK((av.last<0>().bounds() == static_bounds<0>())); + span av; CHECK(av.last<0>().length() == 0); CHECK(av.last(0).length() == 0); } @@ -681,7 +670,6 @@ SUITE(span_tests) { span av = arr; - CHECK((av.subspan<2, 2>().bounds() == static_bounds<2>())); CHECK((av.subspan<2, 2>().length() == 2)); CHECK(av.subspan(2, 2).length() == 2); CHECK(av.subspan(2, 3).length() == 3); @@ -689,14 +677,12 @@ SUITE(span_tests) { span av = arr; - CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); CHECK((av.subspan<0, 0>().length() == 0)); CHECK(av.subspan(0, 0).length() == 0); } { span av = arr; - CHECK((av.subspan<0, 5>().bounds() == static_bounds<5>())); CHECK((av.subspan<0, 5>().length() == 5)); CHECK(av.subspan(0, 5).length() == 5); CHECK_THROW(av.subspan(0, 6).length(), fail_fast); @@ -705,15 +691,14 @@ SUITE(span_tests) { span av = arr; - CHECK((av.subspan<5, 0>().bounds() == static_bounds<0>())); - CHECK((av.subspan<5, 0>().length() == 0)); + CHECK((av.subspan<4, 0>().length() == 0)); + CHECK(av.subspan(4, 0).length() == 0); CHECK(av.subspan(5, 0).length() == 0); CHECK_THROW(av.subspan(6, 0).length(), fail_fast); } { - span av; - CHECK((av.subspan<0, 0>().bounds() == static_bounds<0>())); + span av; CHECK((av.subspan<0, 0>().length() == 0)); CHECK(av.subspan(0, 0).length() == 0); CHECK_THROW((av.subspan<1, 0>().length()), fail_fast); @@ -747,72 +732,7 @@ SUITE(span_tests) for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); } } - - TEST(rank) - { - int arr[2] = {1, 2}; - - { - span s; - CHECK(s.rank() == 1); - } - - { - span s = arr; - CHECK(s.rank() == 1); - } - - int arr2d[1][1] = {}; - { - span s = arr2d; - CHECK(s.rank() == 2); - } - } - - TEST(extent) - { - { - span s; - CHECK(s.extent() == 0); - CHECK(s.extent(0) == 0); - CHECK_THROW(s.extent(1), fail_fast); -#ifdef CONFIRM_COMPILATION_ERRORS - CHECK(s.extent<1>() == 0); -#endif - } - - { - span s; - CHECK(s.extent() == 0); - CHECK(s.extent(0) == 0); - CHECK_THROW(s.extent(1), fail_fast); - } - - { - int arr2d[1][2] = {}; - - span s = arr2d; - CHECK(s.extent() == 1); - CHECK(s.extent<0>() == 1); - CHECK(s.extent<1>() == 2); - CHECK(s.extent(0) == 1); - CHECK(s.extent(1) == 2); - CHECK_THROW(s.extent(3), fail_fast); - } - - { - int arr2d[1][2] = {}; - - span s = arr2d; - CHECK(s.extent() == 0); - CHECK(s.extent<0>() == 0); - CHECK(s.extent<1>() == 2); - CHECK(s.extent(0) == 0); - CHECK(s.extent(1) == 2); - CHECK_THROW(s.extent(3), fail_fast); - } - } - +#if 0 TEST(operator_function_call) { int arr[4] = {1, 2, 3, 4}; -- cgit v1.2.3 From 85939048b457e4808f2f9b8c29d257d53f24fe61 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 18 Mar 2016 16:53:16 -0700 Subject: Test for operator function call. --- tests/span_tests.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 01784f6..9afd943 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -732,7 +732,7 @@ SUITE(span_tests) for (int i = 0; i < 4; ++i) CHECK(av2[i] == i + 2); } } -#if 0 + TEST(operator_function_call) { int arr[4] = {1, 2, 3, 4}; @@ -743,15 +743,15 @@ SUITE(span_tests) CHECK_THROW(s(5), fail_fast); } - int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; - { - span s = arr2d; - CHECK(s(0, 0) == 1); - CHECK(s(1, 2) == 6); + int arr2d[2] = {1, 6}; + span s = arr2d; + CHECK(s(0) == 1); + CHECK(s(1) == 6); + CHECK_THROW(s(2) ,fail_fast); } } - +#if 0 TEST(comparison_operators) { { @@ -864,6 +864,8 @@ SUITE(span_tests) } } +#if 0 + TEST(basics) { auto ptr = as_span(new int[10], 10); -- cgit v1.2.3 From 32d00796ad530d202dcbdaf817d47a2405b31301 Mon Sep 17 00:00:00 2001 From: Sergiy Oryekhov Date: Wed, 23 Mar 2016 16:42:35 -0700 Subject: Adding unittest-cpp as a submodule. --- .gitignore | 1 - .gitmodules | 3 +++ tests/CMakeLists.txt | 4 ++-- tests/unittest-cpp | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .gitmodules create mode 160000 tests/unittest-cpp diff --git a/.gitignore b/.gitignore index ea47eb3..d452689 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -tests/unittest-cpp CMakeFiles tests/CMakeFiles tests/Debug diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d9229ae --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tests/unittest-cpp"] + path = tests/unittest-cpp + url = https://github.com/Microsoft/unittest-cpp.git diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7990ec3..66516fe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,8 +28,8 @@ else() endif() endif() -if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp) - message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp/tests) + execute_process(COMMAND git submodule update --init --recursive WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() function(add_gsl_test name) diff --git a/tests/unittest-cpp b/tests/unittest-cpp new file mode 160000 index 0000000..dc6b908 --- /dev/null +++ b/tests/unittest-cpp @@ -0,0 +1 @@ +Subproject commit dc6b90838014ab985bf3cd74ac17ad9d00e1fbcb -- cgit v1.2.3 From 79030c55863d8a98fb67d549c747c9666b4ef2ec Mon Sep 17 00:00:00 2001 From: Sergiy Oryekhov Date: Wed, 23 Mar 2016 16:53:00 -0700 Subject: Moving submodule init before subdirectory. --- tests/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 66516fe..e6527da 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 2.8.7) project(GSLTests CXX) +if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp/tests) + execute_process(COMMAND git submodule update --init WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +endif() + add_subdirectory(unittest-cpp) include_directories( @@ -28,10 +32,6 @@ else() endif() endif() -if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/unittest-cpp/tests) - execute_process(COMMAND git submodule update --init --recursive WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") -endif() - function(add_gsl_test name) 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++) -- cgit v1.2.3 From 38d8a3f82dea1125068f301db6cf3f1944d8fc61 Mon Sep 17 00:00:00 2001 From: Sergiy Oryekhov Date: Thu, 24 Mar 2016 16:59:45 -0700 Subject: Updating travis script. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c64230..3fed41b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,7 +56,6 @@ install: before_script: - cd ${TRAVIS_BUILD_DIR} - - git clone --depth 1 https://github.com/Microsoft/unittest-cpp tests/unittest-cpp - cmake -H. -Bb -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_INSTALL_PREFIX=$PWD/o -DCMAKE_BUILD_TYPE=$BUILD_TYPE - cmake --build b -- cgit v1.2.3 From a9f0ce2f77e4230275dc7a22f3bef351e251f29c Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 31 Mar 2016 12:01:07 -0700 Subject: Suppressed CppCoreCheck warnings. --- include/gsl_util.h | 6 +++++- include/span.h | 8 +++++++- include/string_span.h | 12 +++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index 316f2a1..edfcce4 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -31,6 +31,9 @@ #pragma push_macro("constexpr") #define constexpr +#pragma warning(push) +#pragma warning(disable: 4127) // conditional expression is constant + // MSVC 2013 workarounds #if _MSC_VER <= 1800 // noexcept is not understood @@ -40,7 +43,6 @@ // turn off some misguided warnings #pragma warning(push) #pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable: 4127) // conditional expression is constant #endif // _MSC_VER <= 1800 @@ -131,6 +133,8 @@ constexpr typename Cont::value_type& at(Cont& cont, size_t index) #ifdef _MSC_VER +#pragma warning(pop) + #undef constexpr #pragma pop_macro("constexpr") diff --git a/include/span.h b/include/span.h index 31c26ad..a612983 100644 --- a/include/span.h +++ b/include/span.h @@ -37,10 +37,16 @@ #ifdef _MSC_VER -// turn off some warnings that are noisy about our Expects statements #pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements #pragma warning(disable : 4127) // conditional expression is constant +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable: 26481 26482 26483 26485 26490 26491 26492 26493 26495) + // No MSVC does constexpr fully yet #pragma push_macro("constexpr") #define constexpr diff --git a/include/string_span.h b/include/string_span.h index 46bf2d4..fdcbf31 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -31,6 +31,14 @@ #pragma push_macro("constexpr") #define constexpr /* nothing */ +#pragma warning(push) + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable: 26481 26482 26483 26485 26490 26491 26492 26493 26495) + + // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -930,12 +938,14 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc } #endif -// VS 2013 workarounds #ifdef _MSC_VER +#pragma warning(pop) + #undef constexpr #pragma pop_macro("constexpr") +// VS 2013 workarounds #if _MSC_VER <= 1800 #ifndef GSL_THROW_ON_CONTRACT_VIOLATION -- cgit v1.2.3 From 9d8866a732a9c2da79079fbd73bb2b933a6af5af Mon Sep 17 00:00:00 2001 From: ericLemanissier Date: Mon, 9 May 2016 13:02:27 +0200 Subject: gsl::at overload for initializer_list initializer_list do not have subscript operator, so the generic container overload of gsl::at fails to compile. This commits adds an overload of gsl::at for initializer_lists, using *(initializer_list::begin()+index) instead of subscript operator --- include/gsl_util.h | 4 ++++ tests/at_tests.cpp | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/gsl_util.h b/include/gsl_util.h index edfcce4..b42b66a 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -128,6 +128,10 @@ template constexpr typename Cont::value_type& at(Cont& cont, size_t index) { Expects(index < cont.size()); return cont[index]; } +template +constexpr const T& at(std::initializer_list cont, size_t index) +{ Expects(index < cont.size()); return *(cont.begin() + index); } + } // namespace gsl diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp index d27dd9d..1a9f814 100644 --- a/tests/at_tests.cpp +++ b/tests/at_tests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using namespace std; using namespace gsl; @@ -52,6 +53,16 @@ SUITE(at_tests) CHECK_THROW(at(a, 4), fail_fast); } + + TEST(InitializerList) + { + std::initializer_list a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } } int main(int, const char *[]) -- cgit v1.2.3 From b94a220c4ad78749d4fc9c8a53d4b0cd48e27820 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 13 May 2016 11:50:25 -0700 Subject: Corrected typo identified in #288. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5743bb3..35e93e4 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ of your GSL source. These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`. -1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\vs14-x86 in this example). +1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\build-x86 in this example). cd GSL md build-x86 -- cgit v1.2.3 From d9d6ff01215fe55d8e0768b2c387b564c808dfa2 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 29 May 2016 13:52:28 -0700 Subject: Added iterators. --- include/span.h | 147 ++++++++++++-- tests/span_tests.cpp | 545 +++++---------------------------------------------- 2 files changed, 179 insertions(+), 513 deletions(-) diff --git a/include/span.h b/include/span.h index 032779c..f0edafa 100644 --- a/include/span.h +++ b/include/span.h @@ -137,6 +137,126 @@ struct is_allowed_element_type_conversion { }; +template +class span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + + span_iterator() : span_iterator(nullptr, 0) {} + span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } + + reference operator*() const { Expects(span_); return (*span_)[index_]; } + pointer operator->() const { Expects(span_); return &((*span_)[index_]); } + + span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } + + span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + span_iterator& operator--() noexcept + { + Expects(span_ && index > 0 && index_ <= span_->length()); + --index_; + return *this; + } + + span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + span_iterator operator+(difference_type n) const noexcept + { + auto ret{*this}; + return ret += n; + } + + span_iterator& operator+=(difference_type n) noexcept + { + index_ += n; + Expects(span_ && index_ >= 0 && index_ <= span_->length()); + return *this; + } + + span_iterator operator-(difference_type n) const noexcept + { + auto ret{*this}; + return ret -= n; + } + + span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + + difference_type operator-(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + reference operator[](difference_type n) const noexcept + { return *(*this + n); } + + bool operator==(const span_iterator& rhs) const noexcept + { return span_ == rhs.span_ && index_ == rhs.index_; } + + bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } + + bool operator<(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } + + bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } + + bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + + bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(m_span, rhs.m_span); + } + +private: + const Span* span_; + ptrdiff_t index_; +}; + +template +span_iterator operator+(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept +{ return rhs + n; } + +template +span_iterator operator-(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept +{ + return rhs - n; +} } // namespace details @@ -155,12 +275,10 @@ public: using index_type = std::ptrdiff_t; using pointer = element_type*; using reference = element_type&; -#if 0 // TODO - using iterator = /*implementation-defined */; - using const_iterator = /* implementation-defined */; + + using iterator = details::span_iterator>; using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; -#endif + constexpr static const index_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor @@ -228,8 +346,7 @@ public: > constexpr span(span&& other) : storage_(reinterpret_cast(other.data()), other.length()) - { - } + {} ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; @@ -293,20 +410,14 @@ public: } constexpr reference operator()(index_type idx) const { return this->operator[](idx); } constexpr pointer data() const noexcept { return storage_.data(); } -#if 0 // TODO + // [span.iter], span iterator support - iterator begin() const noexcept; - iterator end() const noexcept; + iterator begin() const noexcept { return {this, 0}; } + iterator end() const noexcept { return {this, length()}; } - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - - reverse_iterator rbegin() const noexcept; - reverse_iterator rend() const noexcept; + reverse_iterator rbegin() const noexcept { return {this, length()}; } + reverse_iterator rend() const noexcept { return {this, 0}; } - const_reverse_iterator crbegin() const noexcept; - const_reverse_iterator crend() const noexcept; -#endif private: template class extent_type; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 9afd943..74182de 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -751,6 +751,56 @@ SUITE(span_tests) CHECK_THROW(s(2) ,fail_fast); } } + + TEST(iterator) + { + span::iterator it1; + span::iterator it2; + CHECK(it1 == it2); + } + + TEST(begin_end) + { + { + int a[] = { 1, 2, 3, 4 }; + span s = a; + + span::iterator it = s.begin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 1); + + span::iterator beyond = s.end(); + CHECK(it != beyond); + CHECK_THROW(*beyond, fail_fast); + + CHECK(beyond - first == 4); + CHECK(first - first == 0); + CHECK(beyond - beyond == 0); + + ++it; + CHECK(it - first == 1); + CHECK(*it == 2); + *it = 22; + CHECK(*it == 22); + CHECK(beyond - it == 3); + + it = first; + CHECK(it == first); + while (it != s.end()) + { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK(it - beyond == 0); + + for (auto& n : s) + CHECK(n == 5); + } + } + #if 0 TEST(comparison_operators) { @@ -864,476 +914,6 @@ SUITE(span_tests) } } -#if 0 - - TEST(basics) - { - auto ptr = as_span(new int[10], 10); - fill(ptr.begin(), ptr.end(), 99); - for (int num : ptr) { - CHECK(num == 99); - } - - delete[] ptr.data(); - } - - TEST(bounds_checks) - { - int arr[10][2]; - auto av = as_span(arr); - - fill(begin(av), end(av), 0); - - av[2][0] = 1; - av[1][1] = 3; - - // out of bounds - CHECK_THROW(av[1][3] = 3, fail_fast); - CHECK_THROW((av[{1, 3}] = 3), fail_fast); - - CHECK_THROW(av[10][2], fail_fast); - CHECK_THROW((av[{10, 2}]), fail_fast); - } - - void overloaded_func(span exp, int expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - void overloaded_func(span exp, char expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - void fixed_func(span exp, int expected_value) - { - for (auto val : exp) { - CHECK(val == expected_value); - } - } - - TEST(span_parameter_test) - { - auto data = new int[4][3][5]; - - auto av = as_span(data, 4); - - CHECK(av.size() == 60); - - fill(av.begin(), av.end(), 34); - - int count = 0; - for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); - CHECK(count == 34 * 60); - overloaded_func(av, 34); - - overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); - - // fixed_func(av, 34); - delete[] data; - } - - TEST(md_access) - { - auto width = 5, height = 20; - - auto imgSize = width * height; - auto image_ptr = new int[imgSize][3]; - - // size check will be done - auto image_view = - as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); - - iota(image_view.begin(), image_view.end(), 1); - - int expected = 0; - for (auto i = 0; i < height; i++) { - for (auto j = 0; j < width; j++) { - CHECK(expected + 1 == image_view[i][j][0]); - CHECK(expected + 2 == image_view[i][j][1]); - CHECK(expected + 3 == image_view[i][j][2]); - - auto val = image_view[{i, j, 0}]; - CHECK(expected + 1 == val); - val = image_view[{i, j, 1}]; - CHECK(expected + 2 == val); - val = image_view[{i, j, 2}]; - CHECK(expected + 3 == val); - - expected += 3; - } - } - } - - TEST(as_span) - { - { - int* arr = new int[150]; - - auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); - - fill(av.begin(), av.end(), 24); - overloaded_func(av, 24); - - delete[] arr; - - array stdarr{0}; - auto av2 = as_span(stdarr); - overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); - - string str = "ttttttttttttttt"; // size = 15 - auto t = str.data(); - (void) t; - auto av3 = as_span(str); - overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); - } - - { - string str; - span strspan = as_span(str); - (void) strspan; - const string cstr; - span cstrspan = as_span(cstr); - (void) cstrspan; - } - - { - int a[3][4][5]; - auto av = as_span(a); - const int(*b)[4][5]; - b = a; - auto bv = as_span(b, 3); - - CHECK(av == bv); - - const std::array arr = {0.0, 0.0, 0.0}; - auto cv = as_span(arr); - (void) cv; - - vector vec(3); - auto dv = as_span(vec); - (void) dv; - -#ifdef CONFIRM_COMPILATION_ERRORS - auto dv2 = as_span(std::move(vec)); -#endif - } - } - - TEST(empty_spans) - { - { - span empty_av(nullptr); - - CHECK(empty_av.bounds().index_bounds() == index<1>{0}); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) { - (void) v; - CHECK(false); - } - } - - { - span empty_av = {}; - CHECK(empty_av.bounds().index_bounds() == index<1>{0}); - CHECK_THROW(empty_av[0], fail_fast); - CHECK_THROW(empty_av.begin()[0], fail_fast); - CHECK_THROW(empty_av.cbegin()[0], fail_fast); - for (auto& v : empty_av) { - (void) v; - CHECK(false); - } - } - } - - TEST(index_constructor) - { - auto arr = new int[8]; - for (int i = 0; i < 4; ++i) { - arr[2 * i] = 4 + i; - arr[2 * i + 1] = i; - } - - span av(arr, 8); - - ptrdiff_t a[1] = {0}; - index<1> i = a; - - CHECK(av[i] == 4); - - auto av2 = as_span(av, dim<4>(), dim<>(2)); - ptrdiff_t a2[2] = {0, 1}; - index<2> i2 = a2; - - CHECK(av2[i2] == 0); - CHECK(av2[0][i] == 4); - - delete[] arr; - } - - TEST(index_constructors) - { - { - // components of the same type - index<3> i1(0, 1, 2); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - size_t c1 = 1; - index<3> i2(c0, c1, 2); - CHECK(i2[0] == 0); - - // from array - index<3> i3 = {0, 1, 2}; - CHECK(i3[0] == 0); - - // from other index of the same size type - index<3> i4 = i3; - CHECK(i4[0] == 0); - - // default - index<3> i7; - CHECK(i7[0] == 0); - - // default - index<3> i9 = {}; - CHECK(i9[0] == 0); - } - - { - // components of the same type - index<1> i1(0); - CHECK(i1[0] == 0); - - // components of different types - size_t c0 = 0; - index<1> i2(c0); - CHECK(i2[0] == 0); - - // from array - index<1> i3 = {0}; - CHECK(i3[0] == 0); - - // from int - index<1> i4 = 0; - CHECK(i4[0] == 0); - - // from other index of the same size type - index<1> i5 = i3; - CHECK(i5[0] == 0); - - // default - index<1> i8; - CHECK(i8[0] == 0); - - // default - index<1> i9 = {}; - CHECK(i9[0] == 0); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - { - index<3> i1(0, 1); - index<3> i2(0, 1, 2, 3); - index<3> i3 = {0}; - index<3> i4 = {0, 1, 2, 3}; - index<1> i5 = {0, 1}; - } -#endif - } - - TEST(index_operations) - { - ptrdiff_t a[3] = {0, 1, 2}; - ptrdiff_t b[3] = {3, 4, 5}; - index<3> i = a; - index<3> j = b; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - - { - index<3> k = i + j; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 3); - CHECK(k[1] == 5); - CHECK(k[2] == 7); - } - - { - index<3> k = i * 3; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<3> k = 3 * i; - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 0); - CHECK(k[1] == 3); - CHECK(k[2] == 6); - } - - { - index<2> k = details::shift_left(i); - - CHECK(i[0] == 0); - CHECK(i[1] == 1); - CHECK(i[2] == 2); - CHECK(k[0] == 1); - CHECK(k[1] == 2); - } - } - - void iterate_second_column(span av) - { - auto length = av.size() / 2; - - // view to the second column - auto section = av.section({0, 1}, {length, 1}); - - CHECK(section.size() == length); - for (auto i = 0; i < section.size(); ++i) { - CHECK(section[i][0] == av[i][1]); - } - - for (auto i = 0; i < section.size(); ++i) { - auto idx = index<2>{i, 0}; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - - CHECK(section.bounds().index_bounds()[0] == length); - CHECK(section.bounds().index_bounds()[1] == 1); - for (auto i = 0; i < section.bounds().index_bounds()[0]; ++i) { - for (auto j = 0; j < section.bounds().index_bounds()[1]; ++j) { - auto idx = index<2>{i, j}; // avoid braces inside the CHECK macro - CHECK(section[idx] == av[i][1]); - } - } - - size_t check_sum = 0; - for (auto i = 0; i < length; ++i) { - check_sum += av[i][1]; - } - - { - auto idx = 0; - size_t sum = 0; - for (auto num : section) { - CHECK(num == av[idx][1]); - sum += num; - idx++; - } - - CHECK(sum == check_sum); - } - { - size_t idx = length - 1; - size_t sum = 0; - for (auto iter = section.rbegin(); iter != section.rend(); ++iter) { - CHECK(*iter == av[idx][1]); - sum += *iter; - idx--; - } - - CHECK(sum == check_sum); - } - } - - TEST(span_section_iteration) - { - int arr[4][2] = {{4, 0}, {5, 1}, {6, 2}, {7, 3}}; - - // static bounds - { - span av = arr; - iterate_second_column(av); - } - // first bound is dynamic - { - span av = arr; - iterate_second_column(av); - } - // second bound is dynamic - { - span av = arr; - iterate_second_column(av); - } - // both bounds are dynamic - { - span av = arr; - iterate_second_column(av); - } - } - - TEST(dynamic_span_section_iteration) - { - auto height = 4, 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(arr, size); - - // first bound is dynamic - { - span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // second bound is dynamic - { - span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - // both bounds are dynamic - { - span av2 = as_span(av, dim<>(height), dim<>(width)); - iterate_second_column(av2); - } - - delete[] arr; - } - - TEST(span_structure_size) - { - double(*arr)[3][4] = new double[100][3][4]; - span av1(arr, 10); - - struct EffectiveStructure - { - double* v1; - ptrdiff_t v2; - }; - CHECK(sizeof(av1) == sizeof(EffectiveStructure)); - - CHECK_THROW(av1[10][3][4], fail_fast); - - span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); - (void) av2; - } - TEST(fixed_size_conversions) { int arr[] = {1, 2, 3, 4}; @@ -1458,31 +1038,6 @@ SUITE(span_tests) } } - TEST(iterator) - { - int a[] = {1, 2, 3, 4}; - - { - span av = a; - auto wav = as_writeable_bytes(av); - for (auto& b : wav) { - b = byte(0); - } - for (size_t i = 0; i < 4; ++i) { - CHECK(a[i] == 0); - } - } - - { - span av = a; - for (auto& n : av) { - n = 1; - } - for (size_t i = 0; i < 4; ++i) { - CHECK(a[i] == 1); - } - } - } #endif } -- cgit v1.2.3 From 25ff7eca1fc66ff2f0dc71d8aafe1939f57490a6 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 29 May 2016 13:54:19 -0700 Subject: Removed some redundant parens. --- include/span.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/span.h b/include/span.h index f0edafa..187f948 100644 --- a/include/span.h +++ b/include/span.h @@ -254,9 +254,7 @@ span_iterator operator+(typename span_iterator::difference_type n, template span_iterator operator-(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept -{ - return rhs - n; -} +{ return rhs - n; } } // namespace details @@ -289,7 +287,7 @@ public: {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) - { Expects(((!ptr && count == 0) || (ptr && count >= 0))); } + { Expects((!ptr && count == 0) || (ptr && count >= 0)); } constexpr span(pointer firstElem, pointer lastElem) : storage_(firstElem, std::distance(firstElem, lastElem)) -- cgit v1.2.3 From d63c9803da102dcf87979319d6cf366ef38d5bb0 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 29 May 2016 14:05:09 -0700 Subject: Added comparison operators. --- include/span.h | 20 ++++++++++++-------- tests/span_tests.cpp | 28 ++++++++++++++++++---------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/include/span.h b/include/span.h index 187f948..2760121 100644 --- a/include/span.h +++ b/include/span.h @@ -481,26 +481,30 @@ private: }; -#if 0 // TODO // [span.comparison], span comparison operators template -constexpr bool operator==(const span& l, const span& r) const noexcept; +constexpr bool operator==(const span& l, const span& r) noexcept +{ return std::equal(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator!=(const span& l, const span& r) const noexcept; +constexpr bool operator!=(const span& l, const span& r) noexcept +{ return !(l == r); } template -constexpr bool operator<(const span& l, const span& r) const noexcept; +constexpr bool operator<(const span& l, const span& r) noexcept +{ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator<=(const span& l, const span& r) const noexcept; +constexpr bool operator<=(const span& l, const span& r) noexcept +{ return !(l > r); } template -constexpr bool operator>(const span& l, const span& r) const noexcept; +constexpr bool operator>(const span& l, const span& r) noexcept +{ return r < l; } template -constexpr bool operator>=(const span& l, const span& r) const noexcept; -#endif +constexpr bool operator>=(const span& l, const span& r) noexcept +{ return !(l < r); } #if 0 // TODO diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 74182de..694d9a2 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -801,23 +801,30 @@ SUITE(span_tests) } } -#if 0 TEST(comparison_operators) { { - int arr[10][2]; - auto s1 = as_span(arr); - span s2 = s1; - + span s1 = nullptr; + span s2 = nullptr; CHECK(s1 == s2); - - span s3 = as_span(s1, dim<>(20)); - CHECK(s3 == s2 && s3 == s1); + CHECK(!(s1 != s2)); + CHECK(!(s1 < s2)); + CHECK(s1 <= s2); + CHECK(!(s1 > s2)); + CHECK(s1 >= s2); + CHECK(s2 == s1); + CHECK(!(s2 != s1)); + CHECK(!(s2 < s1)); + CHECK(s2 <= s1); + CHECK(!(s2 > s1)); + CHECK(s2 >= s1); } { - auto s1 = nullptr; - auto s2 = nullptr; + int arr[] = {2, 1}; + span s1 = arr; + span s2 = arr; + CHECK(s1 == s2); CHECK(!(s1 != s2)); CHECK(!(s1 < s2)); @@ -914,6 +921,7 @@ SUITE(span_tests) } } +#if 0 TEST(fixed_size_conversions) { int arr[] = {1, 2, 3, 4}; -- cgit v1.2.3 From ba8ebef509653127e74063097c1f7703b8a27185 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 29 May 2016 17:06:29 -0700 Subject: Added span to object rep conversions. --- include/byte.h | 33 +++++++++++++++++++ include/multi_span.h | 4 +-- include/span.h | 34 ++++++++++++++++---- tests/span_tests.cpp | 91 ++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 include/byte.h diff --git a/include/byte.h b/include/byte.h new file mode 100644 index 0000000..c6449aa --- /dev/null +++ b/include/byte.h @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_BYTE_H +#define GSL_BYTE_H + +namespace gsl +{ + // This is a simple definition for now that allows + // use of byte within span<> to be standards-compliant + // + // Ultimately a small language change would allow a more + // robust definition (see WG21 proposal P0257 for details). + using byte = unsigned char; + +} // namespace gsl + +#endif // GSL_BYTE_H \ No newline at end of file diff --git a/include/multi_span.h b/include/multi_span.h index 523f324..3d00c6e 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -21,6 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" +#include "byte.h" #include #include #include @@ -1053,9 +1054,6 @@ template class contiguous_span_iterator; template class general_span_iterator; -enum class byte : std::uint8_t -{ -}; template struct dim diff --git a/include/span.h b/include/span.h index 2760121..7152c2d 100644 --- a/include/span.h +++ b/include/span.h @@ -21,6 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" +#include "byte.h" #include #include #include @@ -126,13 +127,13 @@ struct is_allowed_element_type_conversion }; template -struct is_allowed_element_type_conversion +struct is_allowed_element_type_conversion : std::integral_constant::value> { }; template -struct is_allowed_element_type_conversion +struct is_allowed_element_type_conversion : std::integral_constant { }; @@ -507,14 +508,33 @@ constexpr bool operator>=(const span& l, const span to go to size_t, but older compilers may not see it as constexpr + // and so will fail compilation of the template + template + struct calculate_byte_size : + std::integral_constant(sizeof(ElementType) * static_cast(Extent))> + {}; + + template + struct calculate_byte_size : + std::integral_constant + {}; +} + + // [span.objectrep], views of object representation template -constexpr span as_bytes(span s) noexcept; +constexpr span::value> as_bytes(span s) noexcept +{ return {reinterpret_cast(s.data()), s.size_bytes()}; } -template -constexpr span as_writeable_bytes(span) noexcept; -#endif +template ::value>> +constexpr span::value> as_writeable_bytes(span s) noexcept +{ return {reinterpret_cast(s.data()), s.size_bytes()}; } } // namespace gsl diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 694d9a2..ddae404 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -921,6 +921,69 @@ SUITE(span_tests) } } + TEST(as_bytes) + { + int a[] = {1, 2, 3, 4}; + + { + span s = a; + CHECK(s.length() == 4); + span bs = as_bytes(s); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.length() == s.length_bytes()); + } + + { + span s; + auto bs = as_bytes(s); + CHECK(bs.length() == s.length()); + CHECK(bs.length() == 0); + CHECK(bs.size_bytes() == 0); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.data() == nullptr); + } + + { + span s = a; + auto bs = as_bytes(s); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.length() == s.length_bytes()); + } + } + + TEST(as_writeable_bytes) + { + int a[] = {1, 2, 3, 4}; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + span s = a; + CHECK(s.length() == 4); + span bs = as_writeable_bytes(s); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.length() == s.length_bytes()); +#endif + } + + { + span s; + auto bs = as_writeable_bytes(s); + CHECK(bs.length() == s.length()); + CHECK(bs.length() == 0); + CHECK(bs.size_bytes() == 0); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.data() == nullptr); + } + + { + span s = a; + auto bs = as_writeable_bytes(s); + CHECK(static_cast(bs.data()) == static_cast(s.data())); + CHECK(bs.length() == s.length_bytes()); + } + } + #if 0 TEST(fixed_size_conversions) { @@ -1018,34 +1081,6 @@ SUITE(span_tests) CHECK_THROW(f(), fail_fast); } - TEST(as_writeable_bytes) - { - int a[] = {1, 2, 3, 4}; - - { -#ifdef CONFIRM_COMPILATION_ERRORS - // you should not be able to get writeable bytes for const objects - span av = a; - auto wav = av.as_writeable_bytes(); -#endif - } - - { - span av; - auto wav = as_writeable_bytes(av); - CHECK(wav.length() == av.length()); - CHECK(wav.length() == 0); - CHECK(wav.size_bytes() == 0); - } - - { - span av = a; - auto wav = as_writeable_bytes(av); - CHECK(wav.data() == (byte*) &a[0]); - CHECK(wav.length() == sizeof(a)); - } - } - #endif } -- cgit v1.2.3 From c94a66f4684e38ad599d5de15a5f6ac319395286 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 12 Jun 2016 18:28:19 -0700 Subject: Tightened SFINAE for span to span conversions. --- include/span.h | 91 ++++++++++++++++++++++++++---------------- tests/span_tests.cpp | 109 ++++++++++++++++++++++----------------------------- 2 files changed, 104 insertions(+), 96 deletions(-) diff --git a/include/span.h b/include/span.h index 7152c2d..676fdf2 100644 --- a/include/span.h +++ b/include/span.h @@ -77,6 +77,11 @@ template class span; +// [views.constants], constants +constexpr const std::ptrdiff_t dynamic_extent = -1; + + +// implementation details namespace details { template @@ -96,45 +101,55 @@ struct is_span : is_span_oracle> template struct is_allowed_pointer_conversion - : std::integral_constant::value && - std::is_pointer::value && - std::is_convertible::value + : std::bool_constant< + std::is_pointer::value && + std::is_pointer::value && + std::is_convertible::value > { }; template struct is_allowed_integral_conversion - : std::integral_constant::value && - std::is_integral::value && - sizeof(From) == sizeof(To) && - alignof(From) == alignof(To) && - std::is_convertible::value + : std::bool_constant< + std::is_integral::value && + std::is_integral::value && + sizeof(From) == sizeof(To) && + alignof(From) == alignof(To) && + std::is_convertible::value + > +{ +}; + +template +struct is_allowed_extent_conversion + : std::bool_constant< + From == To || + From == gsl::dynamic_extent || + To == gsl::dynamic_extent > { }; template struct is_allowed_element_type_conversion - : std::integral_constant>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value + : std::bool_constant< + std::is_same>::value || + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value > { }; template struct is_allowed_element_type_conversion - : std::integral_constant::value> + : std::bool_constant::value> { }; template struct is_allowed_element_type_conversion - : std::integral_constant + : std::true_type { }; @@ -191,7 +206,7 @@ public: auto ret{*this}; return ret += n; } - + span_iterator& operator+=(difference_type n) noexcept { index_ += n; @@ -217,10 +232,14 @@ public: } reference operator[](difference_type n) const noexcept - { return *(*this + n); } + { + return *(*this + n); + } bool operator==(const span_iterator& rhs) const noexcept - { return span_ == rhs.span_ && index_ == rhs.index_; } + { + return span_ == rhs.span_ && index_ == rhs.index_; + } bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } @@ -231,9 +250,9 @@ public: } bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } - + bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } - + bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } void swap(span_iterator& rhs) noexcept @@ -250,21 +269,20 @@ private: template span_iterator operator+(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept -{ return rhs + n; } +{ + return rhs + n; +} template span_iterator operator-(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept -{ return rhs - n; } - +{ + return rhs - n; +} } // namespace details -// [views.constants], constants -constexpr const std::ptrdiff_t dynamic_extent = -1; - - // [span], class template span template class span { @@ -330,21 +348,23 @@ public: constexpr span(span&& other) noexcept = default; template ::value && + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value > > constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), other.length()) + : storage_(reinterpret_cast(other.data()), extent_type(other.size())) {} template ::value && + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value > > constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), other.length()) + : storage_(reinterpret_cast(other.data()), extent_type(other.size())) {} ~span() noexcept = default; @@ -418,6 +438,8 @@ public: reverse_iterator rend() const noexcept { return {this, 0}; } private: + constexpr static const bool is_span_type = true; + template class extent_type; @@ -430,10 +452,11 @@ private: constexpr extent_type() noexcept {} template - constexpr extent_type(extent_type) noexcept + constexpr extent_type(extent_type ext) noexcept { - static_assert(Other == Extent, + static_assert(Other == Extent || Other == dynamic_extent, "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Extent); } constexpr extent_type(index_type size) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index ddae404..81784c3 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -395,14 +395,14 @@ SUITE(span_tests) { auto get_an_array = []()->std::array { return{1, 2, 3, 4}; }; - auto take_a_span = [](span s) { (void)s; }; + auto take_a_span = [](span s) { static_cast(s); }; // try to take a temporary std::array take_a_span(get_an_array()); } #endif { auto get_an_array = []() -> std::array { return { 1, 2, 3, 4 }; }; - auto take_a_span = [](span s) { (void)s; }; + auto take_a_span = [](span s) { static_cast(s); }; // try to take a temporary std::array take_a_span(get_an_array()); } @@ -438,7 +438,7 @@ SUITE(span_tests) { auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; - auto take_a_span = [](span s) { (void) s; }; + auto take_a_span = [](span s) { static_cast(s); }; // try to take a temporary std::array take_a_span(get_an_array()); } @@ -482,35 +482,35 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_vector = []() -> std::vector { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_vector()); #endif } { auto get_temp_vector = []() -> std::vector { return{}; }; - auto use_span = [](span s) { (void)s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_vector()); } { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_string = []() -> std::string { return{}; }; - auto use_span = [](span s) { (void)s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_string()); #endif } { auto get_temp_string = []() -> std::string { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_string()); } { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_vector = []() -> const std::vector { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_vector()); #endif } @@ -518,7 +518,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_string = []() -> const std::string { return {}; }; - auto use_span = [](span s) { (void) s; }; + auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_string()); #endif } @@ -536,34 +536,34 @@ SUITE(span_tests) { span avd; span avcd = avd; - (void)avcd; + static_cast(avcd); } { #ifdef CONFIRM_COMPILATION_ERRORS span avd; span avb = avd; - (void) avb; + static_cast(avb); #endif } { span s; span s2 = s; - (void)s2; + static_cast(s2); } { span s; span s2 = s; - (void)s2; + static_cast(s2); } { #ifdef CONFIRM_COMPILATION_ERRORS span s; span s2 = s; - (void)s2; + static_cast(s2); #endif } } @@ -984,72 +984,59 @@ SUITE(span_tests) } } -#if 0 TEST(fixed_size_conversions) { int arr[] = {1, 2, 3, 4}; // converting to an span from an equal size array is ok - span av4 = arr; - CHECK(av4.length() == 4); + span s4 = arr; + CHECK(s4.length() == 4); - // converting to dynamic_range a_v is always ok + // converting to dynamic_range is always ok { - span av = av4; - (void) av; - } - { - span av = arr; - (void) av; + span s = s4; + CHECK(s.length() == s4.length()); + static_cast(s); } // initialization or assignment to static span that REDUCES size is NOT ok #ifdef CONFIRM_COMPILATION_ERRORS { - span av2 = arr; - } - { - span av2 = av4; - } -#endif - - { - span av = arr; - span av2 = av; - (void) av2; + span s = arr; } - -#ifdef CONFIRM_COMPILATION_ERRORS { - span av = arr; - span av2 = av.as_span(dim<2>(), dim<2>()); + span s2 = s4; + static_cast(s2); } #endif + // even when done dynamically { - span av = arr; - span av2 = as_span(av, dim<>(2), dim<>(2)); - auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; - CHECK(workaround_macro()); + span s = arr; + auto f = [&]() { + span s2 = s; + static_cast(s2); + }; + CHECK_THROW(f(), fail_fast); } // but doing so explicitly is ok // you can convert statically { - span av2 = {arr, 2}; - (void) av2; + span s2 = {arr, 2}; + static_cast(s2); } { - span av2 = av4.first<1>(); - (void) av2; + span s1 = s4.first<1>(); + static_cast(s1); } // ...or dynamically { - // NB: implicit conversion to span from span - span av2 = av4.first(1); - (void) av2; + // NB: implicit conversion to span from span + span s1 = s4.first(1); + static_cast(s1); } // initialization or assignment to static span that requires size INCREASE is not ok. @@ -1057,31 +1044,29 @@ SUITE(span_tests) #ifdef CONFIRM_COMPILATION_ERRORS { - span av4 = arr2; + span s3 = arr2; } { - span av2 = arr2; - span av4 = av2; + span s2 = arr2; + span s4a = s2; } #endif { auto f = [&]() { - span av9 = {arr2, 2}; - (void) av9; - }; + span s4 = {arr2, 2}; + static_cast(s4); + }; CHECK_THROW(f(), fail_fast); } - // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one - span av = arr2; + // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one + span av = arr2; auto f = [&]() { - span av2 = av; - (void) av2; + span s4 = av; + static_cast(s4); }; CHECK_THROW(f(), fail_fast); } - -#endif } int main(int, const char* []) { return UnitTest::RunAllTests(); } -- cgit v1.2.3 From 62f30205e5be7e87f1b566ee8395d9363f2ec984 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 14 Jun 2016 20:14:17 -0700 Subject: Additional std::array ctor to support const element cases. --- include/span.h | 5 +++++ tests/span_tests.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/span.h b/include/span.h index 676fdf2..3e0ccc9 100644 --- a/include/span.h +++ b/include/span.h @@ -318,6 +318,11 @@ public: {} template + constexpr span(std::array& arr) + : storage_(&arr[0], extent_type()) + {} + + template ::value>> constexpr span(std::array, N>& arr) : storage_(&arr[0], extent_type()) {} diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 81784c3..5c17552 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -400,6 +400,7 @@ SUITE(span_tests) take_a_span(get_an_array()); } #endif + { auto get_an_array = []() -> std::array { return { 1, 2, 3, 4 }; }; auto take_a_span = [](span s) { static_cast(s); }; @@ -445,6 +446,40 @@ SUITE(span_tests) #endif } + TEST(from_std_array_const_constructor) + { + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); + } +#ifdef CONFIRM_COMPILATION_ERRORS + { + span s{arr}; + CHECK(s.size() == 2 && s.data() == arr.data()); + } + + { + span s{arr}; + CHECK(s.size() == 0 && s.data() == arr.data()); + } + + { + span s{arr}; + } + + { + span s{arr}; + } +#endif + } + TEST(from_container_constructor) { std::vector v = {1, 2, 3}; -- cgit v1.2.3 From 64598bc0f724ae123dc0ecc734cbd0ccc6de552b Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 14 Jun 2016 20:20:09 -0700 Subject: Cleaned up last() functions to match spec. --- include/span.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index 3e0ccc9..00225ba 100644 --- a/include/span.h +++ b/include/span.h @@ -388,7 +388,7 @@ public: constexpr span last() const { Expects(Count >= 0 && Count <= size()); - return{ Count == 0 ? data() : data() + (size() - Count), Count }; + return{ data() + (size() - Count), Count }; } template @@ -408,7 +408,7 @@ public: constexpr span last(index_type count) const { Expects(count >= 0 && count <= size()); - return { count == 0 ? data() : data() + (size() - count), count }; + return { data() + (size() - count), count }; } constexpr span subspan(index_type offset, -- cgit v1.2.3 From 7b00172f0073305cd868cecc2d8ab0e716613b39 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 20 Jun 2016 01:41:49 -0700 Subject: Final sync with D0122R3 version of specification. --- include/span.h | 64 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/include/span.h b/include/span.h index 00225ba..e52e14a 100644 --- a/include/span.h +++ b/include/span.h @@ -164,96 +164,96 @@ public: using typename Base::pointer; using typename Base::difference_type; - span_iterator() : span_iterator(nullptr, 0) {} - span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) + constexpr span_iterator() : span_iterator(nullptr, 0) {} + constexpr span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) { Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); } - reference operator*() const { Expects(span_); return (*span_)[index_]; } - pointer operator->() const { Expects(span_); return &((*span_)[index_]); } + constexpr reference operator*() const { Expects(span_); return (*span_)[index_]; } + constexpr pointer operator->() const { Expects(span_); return &((*span_)[index_]); } - span_iterator& operator++() noexcept + constexpr span_iterator& operator++() noexcept { Expects(span_ && index_ >= 0 && index_ < span_->length()); ++index_; return *this; } - span_iterator operator++(int) noexcept + constexpr span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - span_iterator& operator--() noexcept + constexpr span_iterator& operator--() noexcept { Expects(span_ && index > 0 && index_ <= span_->length()); --index_; return *this; } - span_iterator operator--(int) noexcept + constexpr span_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - span_iterator operator+(difference_type n) const noexcept + constexpr span_iterator operator+(difference_type n) const noexcept { auto ret{*this}; return ret += n; } - span_iterator& operator+=(difference_type n) noexcept + constexpr span_iterator& operator+=(difference_type n) noexcept { index_ += n; Expects(span_ && index_ >= 0 && index_ <= span_->length()); return *this; } - span_iterator operator-(difference_type n) const noexcept + constexpr span_iterator operator-(difference_type n) const noexcept { auto ret{*this}; return ret -= n; } - span_iterator& operator-=(difference_type n) noexcept + constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const span_iterator& rhs) const noexcept + constexpr difference_type operator-(const span_iterator& rhs) const noexcept { Expects(span_ == rhs.span_); return index_ - rhs.index_; } - reference operator[](difference_type n) const noexcept + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const span_iterator& rhs) const noexcept + constexpr bool operator==(const span_iterator& rhs) const noexcept { return span_ == rhs.span_ && index_ == rhs.index_; } - bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } + constexpr bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const span_iterator& rhs) const noexcept + constexpr bool operator<(const span_iterator& rhs) const noexcept { Expects(span_ == rhs.span_); return index_ < rhs.index_; } - bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } + constexpr bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } + constexpr bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } void swap(span_iterator& rhs) noexcept { @@ -267,14 +267,14 @@ private: }; template -span_iterator operator+(typename span_iterator::difference_type n, +constexpr span_iterator operator+(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept { return rhs + n; } template -span_iterator operator-(typename span_iterator::difference_type n, +constexpr span_iterator operator-(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept { return rhs - n; @@ -313,7 +313,7 @@ public: {} template - constexpr span(element_type(&arr)[N]) + constexpr span(element_type(&arr)[N]) noexcept : storage_(&arr[0], extent_type()) {} @@ -496,7 +496,7 @@ private: { public: template - storage_type(pointer data, OtherExtentType ext) + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) {} constexpr inline pointer data() const noexcept @@ -512,27 +512,27 @@ private: // [span.comparison], span comparison operators template -constexpr bool operator==(const span& l, const span& r) noexcept +constexpr bool operator==(const span& l, const span& r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator!=(const span& l, const span& r) noexcept +constexpr bool operator!=(const span& l, const span& r) { return !(l == r); } template -constexpr bool operator<(const span& l, const span& r) noexcept +constexpr bool operator<(const span& l, const span& r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator<=(const span& l, const span& r) noexcept +constexpr bool operator<=(const span& l, const span& r) { return !(l > r); } template -constexpr bool operator>(const span& l, const span& r) noexcept +constexpr bool operator>(const span& l, const span& r) { return r < l; } template -constexpr bool operator>=(const span& l, const span& r) noexcept +constexpr bool operator>=(const span& l, const span& r) { return !(l < r); } @@ -557,11 +557,11 @@ namespace details // [span.objectrep], views of object representation template -constexpr span::value> as_bytes(span s) noexcept +span::value> as_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } template ::value>> -constexpr span::value> as_writeable_bytes(span s) noexcept +span::value> as_writeable_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } } // namespace gsl -- cgit v1.2.3 From b72d7abfb05aab6e3be88d414811473cb9394465 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 24 Jun 2016 04:54:09 -0700 Subject: Added definition of gsl::byte to match proposed std::byte. --- include/byte.h | 46 +++++++++++++++++++++++--- tests/CMakeLists.txt | 1 + tests/byte_tests.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 tests/byte_tests.cpp diff --git a/include/byte.h b/include/byte.h index c6449aa..6e32fef 100644 --- a/include/byte.h +++ b/include/byte.h @@ -23,10 +23,48 @@ namespace gsl { // This is a simple definition for now that allows // use of byte within span<> to be standards-compliant - // - // Ultimately a small language change would allow a more - // robust definition (see WG21 proposal P0257 for details). - using byte = unsigned char; + enum class byte : unsigned char {}; + + template ::value>> + constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept + { return b = byte(static_cast(b) << shift); } + + template ::value>> + constexpr byte operator<<(byte b, IntegerType shift) noexcept + { return byte(static_cast(b) << shift); } + + template ::value>> + constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept + { return b = byte(static_cast(b) >> shift); } + + template ::value>> + constexpr byte operator>> (byte b, IntegerType shift) noexcept + { return byte(static_cast(b) >> shift); } + + constexpr byte& operator|=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) | static_cast(r)); } + + constexpr byte operator|(byte l, byte r) noexcept + { return byte(static_cast(l) + static_cast(r)); } + + constexpr byte& operator&=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) & static_cast(r)); } + + constexpr byte operator&(byte l, byte r) noexcept + { return byte(static_cast(l) & static_cast(r)); } + + constexpr byte& operator^=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) ^ static_cast(r)); } + + constexpr byte operator^(byte l, byte r) noexcept + { return byte(static_cast(l) ^ static_cast(r)); } + + constexpr byte operator~(byte b) noexcept + { return byte(~static_cast(b)); } + + template ::value>> + constexpr IntegerType to_integer(byte b) noexcept { return {b}; } + } // namespace gsl diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9723309..009b52d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -54,3 +54,4 @@ add_gsl_test(notnull_tests) add_gsl_test(assertion_tests) add_gsl_test(utils_tests) add_gsl_test(owner_tests) +add_gsl_test(byte_tests) \ No newline at end of file diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp new file mode 100644 index 0000000..ff91e50 --- /dev/null +++ b/tests/byte_tests.cpp @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ + +SUITE(byte_tests) +{ + TEST(construction) + { + { + byte b = static_cast(4); + CHECK(static_cast(b) == 4); + } + + { + byte b = byte(12); + CHECK(static_cast(b) == 12); + } + + // waiting for C++17 enum class direct initializer support + //{ + // byte b { 14 }; + // CHECK(static_cast(b) == 14); + //} + } + + TEST(bitwise_operations) + { + byte b = byte(0xFF); + + byte a = byte(0x00); + CHECK((b | a) == byte(0xFF)); + CHECK(a == byte(0x00)); + + a |= b; + CHECK(a == byte(0xFF)); + + a = byte(0x01); + CHECK((b & a) == byte(0x01)); + + a &= b; + CHECK(a == byte(0x01)); + + CHECK((b ^ a) == byte(0xFE)); + + CHECK(a == byte(0x01)); + a ^= b; + CHECK(a == byte(0xFE)); + + a = byte(0x01); + CHECK(~a == byte(0xFE)); + + a = byte(0xFF); + CHECK((a << 4) == byte(0xF0)); + CHECK((a >> 4) == byte(0x0F)); + + a <<= 4; + CHECK(a == byte(0xF0)); + a >>= 4; + CHECK(a == byte(0x0F)); + } +} +} + +int main(int, const char* []) { return UnitTest::RunAllTests(); } -- cgit v1.2.3 From 267472449c2ff450aff9ae068d4a8c93651f310f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Sun, 26 Jun 2016 17:00:56 +0300 Subject: Renamed byte header and tidied up string_span dependencies. --- include/byte.h | 71 ----------------- include/gsl_byte.h | 71 +++++++++++++++++ include/multi_span.h | 2 +- include/span.h | 126 +++++++++++++++++++++++------ include/string_span.h | 187 ++++++++++++++++++++++---------------------- tests/byte_tests.cpp | 2 +- tests/string_span_tests.cpp | 28 +++---- 7 files changed, 279 insertions(+), 208 deletions(-) delete mode 100644 include/byte.h create mode 100644 include/gsl_byte.h diff --git a/include/byte.h b/include/byte.h deleted file mode 100644 index 6e32fef..0000000 --- a/include/byte.h +++ /dev/null @@ -1,71 +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_BYTE_H -#define GSL_BYTE_H - -namespace gsl -{ - // This is a simple definition for now that allows - // use of byte within span<> to be standards-compliant - enum class byte : unsigned char {}; - - template ::value>> - constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept - { return b = byte(static_cast(b) << shift); } - - template ::value>> - constexpr byte operator<<(byte b, IntegerType shift) noexcept - { return byte(static_cast(b) << shift); } - - template ::value>> - constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept - { return b = byte(static_cast(b) >> shift); } - - template ::value>> - constexpr byte operator>> (byte b, IntegerType shift) noexcept - { return byte(static_cast(b) >> shift); } - - constexpr byte& operator|=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) | static_cast(r)); } - - constexpr byte operator|(byte l, byte r) noexcept - { return byte(static_cast(l) + static_cast(r)); } - - constexpr byte& operator&=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) & static_cast(r)); } - - constexpr byte operator&(byte l, byte r) noexcept - { return byte(static_cast(l) & static_cast(r)); } - - constexpr byte& operator^=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) ^ static_cast(r)); } - - constexpr byte operator^(byte l, byte r) noexcept - { return byte(static_cast(l) ^ static_cast(r)); } - - constexpr byte operator~(byte b) noexcept - { return byte(~static_cast(b)); } - - template ::value>> - constexpr IntegerType to_integer(byte b) noexcept { return {b}; } - - -} // namespace gsl - -#endif // GSL_BYTE_H \ No newline at end of file diff --git a/include/gsl_byte.h b/include/gsl_byte.h new file mode 100644 index 0000000..6e32fef --- /dev/null +++ b/include/gsl_byte.h @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_BYTE_H +#define GSL_BYTE_H + +namespace gsl +{ + // This is a simple definition for now that allows + // use of byte within span<> to be standards-compliant + enum class byte : unsigned char {}; + + template ::value>> + constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept + { return b = byte(static_cast(b) << shift); } + + template ::value>> + constexpr byte operator<<(byte b, IntegerType shift) noexcept + { return byte(static_cast(b) << shift); } + + template ::value>> + constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept + { return b = byte(static_cast(b) >> shift); } + + template ::value>> + constexpr byte operator>> (byte b, IntegerType shift) noexcept + { return byte(static_cast(b) >> shift); } + + constexpr byte& operator|=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) | static_cast(r)); } + + constexpr byte operator|(byte l, byte r) noexcept + { return byte(static_cast(l) + static_cast(r)); } + + constexpr byte& operator&=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) & static_cast(r)); } + + constexpr byte operator&(byte l, byte r) noexcept + { return byte(static_cast(l) & static_cast(r)); } + + constexpr byte& operator^=(byte& l, byte r) noexcept + { return l = byte(static_cast(l) ^ static_cast(r)); } + + constexpr byte operator^(byte l, byte r) noexcept + { return byte(static_cast(l) ^ static_cast(r)); } + + constexpr byte operator~(byte b) noexcept + { return byte(~static_cast(b)); } + + template ::value>> + constexpr IntegerType to_integer(byte b) noexcept { return {b}; } + + +} // namespace gsl + +#endif // GSL_BYTE_H \ No newline at end of file diff --git a/include/multi_span.h b/include/multi_span.h index 3d00c6e..c4a0eac 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -21,7 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" -#include "byte.h" +#include "gsl_byte.h" #include #include #include diff --git a/include/span.h b/include/span.h index e52e14a..053b488 100644 --- a/include/span.h +++ b/include/span.h @@ -21,7 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" -#include "byte.h" +#include "gsl_byte.h" #include #include #include @@ -154,18 +154,21 @@ struct is_allowed_element_type_conversion }; template -class span_iterator - : public std::iterator +class const_span_iterator { - using Base = std::iterator; - public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using const_pointer = std::add_const_t; + using pointer = const_pointer; + + using const_reference = std::add_const_t; + using reference = const_reference; - constexpr span_iterator() : span_iterator(nullptr, 0) {} - constexpr span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) + constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} + constexpr const_span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) { Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); } @@ -173,59 +176,59 @@ public: constexpr reference operator*() const { Expects(span_); return (*span_)[index_]; } constexpr pointer operator->() const { Expects(span_); return &((*span_)[index_]); } - constexpr span_iterator& operator++() noexcept + constexpr const_span_iterator& operator++() noexcept { Expects(span_ && index_ >= 0 && index_ < span_->length()); ++index_; return *this; } - constexpr span_iterator operator++(int) noexcept + constexpr const_span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - constexpr span_iterator& operator--() noexcept + constexpr const_span_iterator& operator--() noexcept { Expects(span_ && index > 0 && index_ <= span_->length()); --index_; return *this; } - constexpr span_iterator operator--(int) noexcept + constexpr const_span_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - constexpr span_iterator operator+(difference_type n) const noexcept + constexpr const_span_iterator operator+(difference_type n) const noexcept { auto ret{*this}; return ret += n; } - constexpr span_iterator& operator+=(difference_type n) noexcept + constexpr const_span_iterator& operator+=(difference_type n) noexcept { index_ += n; Expects(span_ && index_ >= 0 && index_ <= span_->length()); return *this; } - constexpr span_iterator operator-(difference_type n) const noexcept + constexpr const_span_iterator operator-(difference_type n) const noexcept { auto ret{*this}; return ret -= n; } - constexpr span_iterator& operator-=(difference_type n) noexcept + constexpr const_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - constexpr difference_type operator-(const span_iterator& rhs) const noexcept + constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept { Expects(span_ == rhs.span_); return index_ - rhs.index_; @@ -236,26 +239,26 @@ public: return *(*this + n); } - constexpr bool operator==(const span_iterator& rhs) const noexcept + constexpr bool operator==(const const_span_iterator& rhs) const noexcept { return span_ == rhs.span_ && index_ == rhs.index_; } - constexpr bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } + constexpr bool operator!=(const const_span_iterator& rhs) const noexcept { return !(*this == rhs); } - constexpr bool operator<(const span_iterator& rhs) const noexcept + constexpr bool operator<(const const_span_iterator& rhs) const noexcept { Expects(span_ == rhs.span_); return index_ < rhs.index_; } - constexpr bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } + constexpr bool operator<=(const const_span_iterator& rhs) const noexcept { return !(rhs < *this); } - constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + constexpr bool operator>(const const_span_iterator& rhs) const noexcept { return rhs < *this; } - constexpr bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } + constexpr bool operator>=(const const_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(span_iterator& rhs) noexcept + void swap(const_span_iterator& rhs) noexcept { std::swap(index_, rhs.index_); std::swap(m_span, rhs.m_span); @@ -266,6 +269,75 @@ private: ptrdiff_t index_; }; + +template +class span_iterator : public const_span_iterator +{ + using base_type = const_span_iterator; + +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using pointer = value_type*; + using reference = value_type&; + + constexpr span_iterator() : base_type() {} + constexpr span_iterator(const Span* span, typename Span::index_type index) : base_type(span, index) {} + + constexpr reference operator*() const { return reinterpret_cast(base_type::operator*()); } + constexpr pointer operator->() const { return reinterpret_cast(base_type::operator->()); } + + constexpr span_iterator& operator++() noexcept { base_type::operator++(); return *this; } + + constexpr span_iterator operator++(int) noexcept { return base_type::operator++(1); } + + constexpr span_iterator& operator--() noexcept { base_type::operator--(); return *this; } + + constexpr span_iterator operator--(int) noexcept { return base_type::operator--(1); } + + constexpr span_iterator operator+(difference_type n) const noexcept { return base_type::operator+(n); } + + constexpr span_iterator& operator+=(difference_type n) noexcept { return base_type::operator+=(n); } + + constexpr span_iterator operator-(difference_type n) const noexcept { return base_type::operator-(n); } + + constexpr span_iterator& operator-=(difference_type n) noexcept { return base_type::operator-=(n); } + + constexpr difference_type operator-(const span_iterator& rhs) const noexcept { return base_type::operator-(rhs); } + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const span_iterator& rhs) const noexcept { return base_type::operator==(rhs); } + + constexpr bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const span_iterator& rhs) const noexcept { return base_type::operator<(rhs); } + + constexpr bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } + + constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } +}; + +template +constexpr const_span_iterator operator+(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +constexpr const_span_iterator operator-(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept +{ + return rhs - n; +} + template constexpr span_iterator operator+(typename span_iterator::difference_type n, const span_iterator& rhs) noexcept @@ -294,7 +366,9 @@ public: using reference = element_type&; using iterator = details::span_iterator>; + using const_iterator = details::span_iterator; using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; constexpr static const index_type extent = Extent; diff --git a/include/string_span.h b/include/string_span.h index 69c3a81..8aba362 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -21,7 +21,7 @@ #include "gsl_assert.h" #include "gsl_util.h" -#include "multi_span.h" +#include "span.h" #include #include @@ -72,19 +72,19 @@ namespace gsl // (sometimes needlessly) break existing programs when introduced. // -template +template using basic_zstring = CharT*; -template +template using czstring = basic_zstring; -template +template using cwzstring = basic_zstring; -template +template using zstring = basic_zstring; -template +template using wzstring = basic_zstring; // @@ -96,7 +96,7 @@ using wzstring = basic_zstring; // Will fail-fast if sentinel cannot be found before max elements are examined. // template -multi_span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) +span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { auto cur = seq; while ((cur - seq) < max && *cur != Sentinel) ++cur; @@ -111,34 +111,34 @@ multi_span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIF // the limit of size_type. // template -inline multi_span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); } // TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation -inline multi_span ensure_z(char* const& sz, std::ptrdiff_t max) +inline span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline multi_span ensure_z(const char* const& sz, std::ptrdiff_t max) +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline multi_span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); return{ sz, static_cast(len) }; } -inline multi_span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); @@ -146,10 +146,10 @@ inline multi_span ensure_z(const wchar_t* const& s } template -multi_span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } +span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } template -multi_span::type, dynamic_range> ensure_z(Cont& cont) +span::type, dynamic_extent> ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } @@ -216,9 +216,7 @@ namespace details // // string_span and relatives // -// Note that Extent is always single-dimension only -// -template +template class basic_string_span { public: @@ -227,8 +225,7 @@ public: using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t; - using bounds_type = static_bounds; - using impl_type = multi_span; + using impl_type = span; using size_type = ptrdiff_t; using iterator = typename impl_type::iterator; @@ -296,52 +293,52 @@ public: {} // from containers. Containers must have .size() and .data() function signatures - template ::value - && !details::is_basic_string_span::value - && !(!std::is_const::value && std::is_const::value) // no converting const containers to non-const span - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> + template ::value + && !details::is_span::value + && std::is_convertible::value + && std::is_convertible().data())>::value> > - constexpr basic_string_span(Cont& cont) - : span_(cont.data(), cont.size()) - {} + constexpr basic_string_span(Cont& cont) : span_(cont.data(), cont.size()) {} // disallow creation from temporary containers and strings - template ::value - && !details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> - > - basic_string_span(Cont&& cont) = delete; + template ::value + && !details::is_span::value + && std::is_convertible::value + && std::is_convertible().data())>::value> + > + constexpr basic_string_span(const Cont& cont) : span_(cont.data(), cont.size()) {} #ifndef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE // from span template ::value - && std::is_convertible, bounds_type>::value> + std::is_convertible, impl_type>::value + > > - constexpr basic_string_span(multi_span other) noexcept + constexpr basic_string_span(span other) noexcept : span_(other) {} #else // from span - constexpr basic_string_span(multi_span other) noexcept + constexpr basic_string_span(span other) noexcept : span_(other) {} - template , value_type>::value>> - constexpr basic_string_span(multi_span, Extent> other) noexcept + template , value_type>::value>> + constexpr basic_string_span(span, Extent> other) noexcept : span_(other) {} #endif // from string_span template , - typename Dummy = std::enable_if_t::value && std::is_convertible::value> + typename = std::enable_if_t< + std::is_convertible::impl_type, impl_type>::value + > > constexpr basic_string_span(basic_string_span other) noexcept : span_(other.data(), other.length()) @@ -359,7 +356,7 @@ public: return{ span_.template first() }; } - constexpr basic_string_span first(size_type count) const noexcept + constexpr basic_string_span first(size_type count) const noexcept { return{ span_.first(count) }; } @@ -371,7 +368,7 @@ public: return{ span_.template last() }; } - constexpr basic_string_span last(size_type count) const noexcept + constexpr basic_string_span last(size_type count) const noexcept { return{ span_.last(count) }; } @@ -383,7 +380,7 @@ public: return{ span_.template subspan() }; } - constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_range) const noexcept + constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_extent) const noexcept { return{ span_.subspan(offset, count) }; } @@ -478,16 +475,16 @@ private: impl_type span_; }; -template +template using string_span = basic_string_span; -template +template using cstring_span = basic_string_span; -template +template using wstring_span = basic_string_span; -template +template using cwstring_span = basic_string_span; // @@ -527,7 +524,7 @@ inline std::wstring to_string(wstring_span<> view) // zero-terminated string span, used to convert // zero-terminated spans to legacy strings -template +template class basic_zstring_span { public: @@ -540,14 +537,14 @@ public: using zstring_type = basic_zstring; using const_zstring_type = basic_zstring; - using impl_type = multi_span; + using impl_type = span; using string_span_type = basic_string_span; - constexpr basic_zstring_span(impl_type multi_span) noexcept - : span_(multi_span) + constexpr basic_zstring_span(impl_type s) noexcept + : span_(s) { // expects a zero-terminated span - Expects(multi_span[multi_span.size() - 1] == '\0'); + Expects(s[s.size() - 1] == '\0'); } // copy @@ -588,22 +585,22 @@ private: impl_type span_; }; -template +template using zstring_span = basic_zstring_span; -template +template using wzstring_span = basic_zstring_span; -template +template using czstring_span = basic_zstring_span; -template +template using cwzstring_span = basic_zstring_span; } // namespace GSL // operator == -template , Extent>>::value> > @@ -617,7 +614,7 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc #endif } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -637,10 +634,10 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -651,10 +648,10 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -667,7 +664,7 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #endif // operator != -template , Extent>>::value> > @@ -676,7 +673,7 @@ bool operator!=(gsl::basic_string_span one, const T& other) noexc return !(one == other); } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -691,10 +688,10 @@ bool operator!=(const T& one, gsl::basic_string_span other) noexc // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -704,10 +701,10 @@ bool operator!=(gsl::basic_string_span one, const T& other) noexc return !(one == other); } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -719,7 +716,7 @@ bool operator!=(const T& one, gsl::basic_string_span other) noexc #endif // operator< -template , Extent>>::value> > @@ -729,7 +726,7 @@ bool operator<(gsl::basic_string_span one, const T& other) noexce return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -745,10 +742,10 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -759,10 +756,10 @@ bool operator<(gsl::basic_string_span one, const T& other) noexce return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -775,7 +772,7 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce #endif // operator <= -template , Extent>>::value> > @@ -784,7 +781,7 @@ bool operator<=(gsl::basic_string_span one, const T& other) noexc return !(other < one); } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -799,10 +796,10 @@ bool operator<=(const T& one, gsl::basic_string_span other) noexc // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -812,10 +809,10 @@ bool operator<=(gsl::basic_string_span one, const T& other) noexc return !(other < one); } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -827,7 +824,7 @@ bool operator<=(const T& one, gsl::basic_string_span other) noexc #endif // operator> -template , Extent>>::value> > @@ -836,7 +833,7 @@ bool operator>(gsl::basic_string_span one, const T& other) noexce return other < one; } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -851,10 +848,10 @@ bool operator>(const T& one, gsl::basic_string_span other) noexce // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -864,10 +861,10 @@ bool operator>(gsl::basic_string_span one, const T& other) noexce return other < one; } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -879,7 +876,7 @@ bool operator>(const T& one, gsl::basic_string_span other) noexce #endif // operator >= -template , Extent>>::value> > @@ -888,7 +885,7 @@ bool operator>=(gsl::basic_string_span one, const T& other) noexc return !(one < other); } -template , Extent>>::value && !gsl::details::is_basic_string_span::value> @@ -903,10 +900,10 @@ bool operator>=(const T& one, gsl::basic_string_span other) noexc // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> @@ -916,10 +913,10 @@ bool operator>=(gsl::basic_string_span one, const T& other) noexc return !(one < other); } -template ::value + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value> diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index ff91e50..19456fd 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 6876253..f380be3 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -142,7 +142,7 @@ SUITE(string_span_tests) const char* ptr = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::multi_span sp = ensure_z("Hello"); + gsl::span sp = ensure_z("Hello"); // comparison to literal CHECK(span == cstring_span<>("Hello")); @@ -182,7 +182,7 @@ SUITE(string_span_tests) char* ptr = ar; std::string str = "Hello"; std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::multi_span sp = ensure_z(ar1); + gsl::span sp = ensure_z(ar1); // comparison to static array with no null termination CHECK(span == string_span<>(ar)); @@ -216,7 +216,7 @@ SUITE(string_span_tests) const char ar2[10] = "Hello"; const std::string str = "Hello"; const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::multi_span sp = ensure_z("Hello"); + gsl::span sp = ensure_z("Hello"); cstring_span<> span = "Hello"; @@ -253,7 +253,7 @@ SUITE(string_span_tests) char* _ptr = _ar; std::string _str = "Hello"; std::vector _vec = { 'H', 'e', 'l', 'l', 'o' }; - gsl::multi_span _sp{ _ar, 5 }; + gsl::span _sp{ _ar, 5 }; CHECK(span == _ar); CHECK(span == _ar1); @@ -447,7 +447,7 @@ SUITE(string_span_tests) // from span of a final extent { - multi_span sp = "Hello"; + span sp = "Hello"; cstring_span<> span = sp; CHECK(span.length() == 6); } @@ -455,7 +455,7 @@ SUITE(string_span_tests) // from const span of a final extent to non-const string_span #ifdef CONFIRM_COMPILATION_ERRORS { - multi_span sp = "Hello"; + span sp = "Hello"; string_span<> span = sp; CHECK(span.length() == 6); } @@ -568,7 +568,7 @@ SUITE(string_span_tests) // from const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const multi_span inner = vec; + const span inner = vec; cstring_span<> span = inner; CHECK(span.length() == 5); } @@ -576,7 +576,7 @@ SUITE(string_span_tests) // from non-const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - multi_span inner = vec; + span inner = vec; cstring_span<> span = inner; CHECK(span.length() == 5); } @@ -675,7 +675,7 @@ SUITE(string_span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const multi_span inner = vec; + const span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -684,7 +684,7 @@ SUITE(string_span_tests) // from non-const span { std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - multi_span inner = vec; + span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); } @@ -693,7 +693,7 @@ SUITE(string_span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS const std::vector vec = { 'H', 'e', 'l', 'l', 'o' }; - const multi_span inner = vec; + const span inner = vec; string_span<> span = inner; CHECK(span.length() == 5); #endif @@ -746,7 +746,7 @@ SUITE(string_span_tests) T create() { return T{}; } template - void use(basic_string_span s) {} + void use(basic_string_span s) {} TEST(MoveConstructors) { @@ -769,12 +769,12 @@ SUITE(string_span_tests) // move span { - multi_span span = ensure_z("Hello"); + span span = ensure_z("Hello"); cstring_span<> span1 = std::move(span); CHECK(span1.length() == 5); } { - multi_span span = ensure_z("Hello"); + span span = ensure_z("Hello"); cstring_span<> span2 = move_wrapper(std::move(span)); CHECK(span2.length() == 5); } -- cgit v1.2.3 From 8855c59579bb0de5212210662ed8aebff3582d41 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 15 Jul 2016 17:31:40 -0700 Subject: Added basic compile support for MSVC 2013 to byte. --- include/gsl_byte.h | 32 ++++++++++++++++++++++++++++++++ tests/byte_tests.cpp | 1 + tests/span_tests.cpp | 9 +++++++++ 3 files changed, 42 insertions(+) diff --git a/include/gsl_byte.h b/include/gsl_byte.h index 6e32fef..e2c79c9 100644 --- a/include/gsl_byte.h +++ b/include/gsl_byte.h @@ -19,6 +19,23 @@ #ifndef GSL_BYTE_H #define GSL_BYTE_H +#ifdef _MSC_VER + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 + +// constexpr is not understood +#pragma push_macro("constexpr") +#define constexpr + +// noexcept is not understood +#pragma push_macro("noexcept") +#define noexcept + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + namespace gsl { // This is a simple definition for now that allows @@ -68,4 +85,19 @@ namespace gsl } // namespace gsl + +#ifdef _MSC_VER + +#if _MSC_VER <= 1800 + +#undef constexpr +#pragma pop_macro("constexpr") + +#undef noexcept +#pragma pop_macro("noexcept") + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + #endif // GSL_BYTE_H \ No newline at end of file diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 19456fd..5d4e7f6 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -87,6 +87,7 @@ SUITE(byte_tests) CHECK(a == byte(0x0F)); } } + } int main(int, const char* []) { return UnitTest::RunAllTests(); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 5c17552..ae3c32e 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -794,6 +794,15 @@ SUITE(span_tests) CHECK(it1 == it2); } + TEST(const_iterator) + { + span::const_iterator it1; + span::const_iterator it2; + CHECK(it1 == it2); + + + } + TEST(begin_end) { { -- cgit v1.2.3 From 30a038ca6a159c259da342e6dec24527d06fef76 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 18 Jul 2016 11:38:01 -0700 Subject: Added tests for span iterators, fixed implementation. --- include/gsl.h | 6 +-- include/span.h | 16 ++++--- tests/span_tests.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 13 deletions(-) diff --git a/include/gsl.h b/include/gsl.h index 4eb555b..f4c9178 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -19,10 +19,10 @@ #ifndef GSL_GSL_H #define GSL_GSL_H -#include "gsl_assert.h" // Ensures/Expects -#include "gsl_util.h" // finally()/narrow()/narrow_cast()... +#include "gsl_assert.h" // Ensures/Expects +#include "gsl_util.h" // finally()/narrow()/narrow_cast()... #include "span.h" // span -#include "multi_span.h" // multi_span, strided_span... +#include "multi_span.h" // multi_span, strided_span... #include "string_span.h" // zstring, string_span, zstring_builder... #include diff --git a/include/span.h b/include/span.h index 053b488..3f3b8fe 100644 --- a/include/span.h +++ b/include/span.h @@ -192,7 +192,7 @@ public: constexpr const_span_iterator& operator--() noexcept { - Expects(span_ && index > 0 && index_ <= span_->length()); + Expects(span_ && index_ > 0 && index_ <= span_->length()); --index_; return *this; } @@ -366,7 +366,7 @@ public: using reference = element_type&; using iterator = details::span_iterator>; - using const_iterator = details::span_iterator; + using const_iterator = details::const_span_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -512,13 +512,17 @@ public: // [span.iter], span iterator support iterator begin() const noexcept { return {this, 0}; } iterator end() const noexcept { return {this, length()}; } + + const_iterator cbegin() const noexcept { return {this, 0}; } + const_iterator cend() const noexcept { return {this, length()}; } - reverse_iterator rbegin() const noexcept { return {this, length()}; } - reverse_iterator rend() const noexcept { return {this, 0}; } + reverse_iterator rbegin() const noexcept { return reverse_iterator{{this, length()}}; } + reverse_iterator rend() const noexcept { return reverse_iterator{{this, 0}}; } -private: - constexpr static const bool is_span_type = true; + const_reverse_iterator crbegin() const noexcept { return reverse_iterator{{this, length()}}; } + const_reverse_iterator crend() const noexcept { return reverse_iterator{{this, 0}}; } +private: template class extent_type; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index ae3c32e..7652ff9 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -799,8 +799,6 @@ SUITE(span_tests) span::const_iterator it1; span::const_iterator it2; CHECK(it1 == it2); - - } TEST(begin_end) @@ -809,12 +807,12 @@ SUITE(span_tests) int a[] = { 1, 2, 3, 4 }; span s = a; - span::iterator it = s.begin(); + auto it = s.begin(); auto first = it; CHECK(it == first); CHECK(*it == 1); - span::iterator beyond = s.end(); + auto beyond = s.end(); CHECK(it != beyond); CHECK_THROW(*beyond, fail_fast); @@ -845,6 +843,132 @@ SUITE(span_tests) } } + TEST(cbegin_cend) + { + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.cbegin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 1); + + auto beyond = s.cend(); + CHECK(it != beyond); + CHECK_THROW(*beyond, fail_fast); + + CHECK(beyond - first == 4); + CHECK(first - first == 0); + CHECK(beyond - beyond == 0); + + ++it; + CHECK(it - first == 1); + CHECK(*it == 2); + *it = 22; + CHECK(*it == 22); + CHECK(beyond - it == 3); + + it = first; + CHECK(it == first); + while (it != s.cend()) + { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK(it - beyond == 0); + + for (auto& n : s) + CHECK(n == 5); + } + } + + TEST(rbegin_rend) + { + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.rbegin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 4); + + auto beyond = s.rend(); + CHECK(it != beyond); + CHECK_THROW(*beyond, fail_fast); + + CHECK(beyond - first == 4); + CHECK(first - first == 0); + CHECK(beyond - beyond == 0); + + ++it; + CHECK(it - first == 1); + CHECK(*it == 3); + *it = 22; + CHECK(*it == 22); + CHECK(beyond - it == 3); + + it = first; + CHECK(it == first); + while (it != s.rend()) + { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK(it - beyond == 0); + + for (auto& n : s) + CHECK(n == 5); + } + } + + TEST(crbegin_crend) + { + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.crbegin(); + auto first = it; + CHECK(it == first); + CHECK(*it == 4); + + auto beyond = s.crend(); + CHECK(it != beyond); + CHECK_THROW(*beyond, fail_fast); + + CHECK(beyond - first == 4); + CHECK(first - first == 0); + CHECK(beyond - beyond == 0); + + ++it; + CHECK(it - first == 1); + CHECK(*it == 3); + *it = 22; + CHECK(*it == 22); + CHECK(beyond - it == 3); + + it = first; + CHECK(it == first); + while (it != s.crend()) + { + *it = 5; + ++it; + } + + CHECK(it == beyond); + CHECK(it - beyond == 0); + + for (auto& n : s) + CHECK(n == 5); + } + } + TEST(comparison_operators) { { -- cgit v1.2.3 From 520c72d777f485775439f4b98b3b7e86c07501e2 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 18 Jul 2016 12:00:33 -0700 Subject: Tidied up assertion. --- include/span.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/span.h b/include/span.h index 3f3b8fe..6fe3bff 100644 --- a/include/span.h +++ b/include/span.h @@ -212,8 +212,8 @@ public: constexpr const_span_iterator& operator+=(difference_type n) noexcept { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); index_ += n; - Expects(span_ && index_ >= 0 && index_ <= span_->length()); return *this; } -- cgit v1.2.3 From 0c1b6717c12477cbf9f744e3b011ae1711256e69 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 20 Jul 2016 08:52:09 -0700 Subject: Weakened cast in span::iterator. --- include/span.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index 6fe3bff..fbed6f7 100644 --- a/include/span.h +++ b/include/span.h @@ -286,8 +286,8 @@ public: constexpr span_iterator() : base_type() {} constexpr span_iterator(const Span* span, typename Span::index_type index) : base_type(span, index) {} - constexpr reference operator*() const { return reinterpret_cast(base_type::operator*()); } - constexpr pointer operator->() const { return reinterpret_cast(base_type::operator->()); } + constexpr reference operator*() const { return const_cast(base_type::operator*()); } + constexpr pointer operator->() const { return const_cast(base_type::operator->()); } constexpr span_iterator& operator++() noexcept { base_type::operator++(); return *this; } -- cgit v1.2.3 From 5e7771f879b6496dc10f17f94aace3faf70c6f71 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 20 Jul 2016 09:00:10 -0700 Subject: Slight tidy-up to precondition checking in span. --- include/span.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/span.h b/include/span.h index fbed6f7..13e57ae 100644 --- a/include/span.h +++ b/include/span.h @@ -380,7 +380,7 @@ public: {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) - { Expects((!ptr && count == 0) || (ptr && count >= 0)); } + {} constexpr span(pointer firstElem, pointer lastElem) : storage_(firstElem, std::distance(firstElem, lastElem)) @@ -575,7 +575,8 @@ private: public: template constexpr storage_type(pointer data, OtherExtentType ext) - : ExtentType(ext), data_(data) {} + : ExtentType(ext), data_(data) + { Expects((!data && size() == 0) || (data && size() >= 0)); } constexpr inline pointer data() const noexcept { return data_; } -- cgit v1.2.3 From f2ab3a5c546385df138063235ad61e1ec1ee7845 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 20 Jul 2016 09:24:49 -0700 Subject: Tidying up noexcepts. --- include/span.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/span.h b/include/span.h index 13e57ae..96164b6 100644 --- a/include/span.h +++ b/include/span.h @@ -392,17 +392,17 @@ public: {} template - constexpr span(std::array& arr) + constexpr span(std::array& arr) noexcept : storage_(&arr[0], extent_type()) {} template ::value>> - constexpr span(std::array, N>& arr) + constexpr span(std::array, N>& arr) noexcept : storage_(&arr[0], extent_type()) {} template ::value>> - constexpr span(const std::array, N>& arr) + constexpr span(const std::array, N>& arr) noexcept : storage_(&arr[0], extent_type()) {} @@ -504,7 +504,7 @@ public: constexpr reference operator[](index_type idx) const { Expects(idx >= 0 && idx < storage_.size()); - return storage_.data()[idx]; + return data()[idx]; } constexpr reference operator()(index_type idx) const { return this->operator[](idx); } constexpr pointer data() const noexcept { return storage_.data(); } -- cgit v1.2.3 From ca4cdd80deabb7145969dcbca67e44ea00b2bd27 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 20 Jul 2016 12:47:21 -0700 Subject: 64-bit clean fixes. --- tests/span_tests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 7652ff9..77b799a 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -131,23 +131,23 @@ SUITE(span_tests) TEST(from_nullptr_length_constructor) { { - span s{nullptr, 0}; + span s{nullptr, static_cast::index_type>(0)}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + span cs{nullptr, static_cast::index_type>(0)}; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - span s{nullptr, 0}; + span s{nullptr, static_cast::index_type>(0)}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + span cs{nullptr, static_cast::index_type>(0)}; CHECK(cs.length() == 0 && cs.data() == nullptr); } { - auto workaround_macro = []() { span s{ nullptr, 0 }; }; + auto workaround_macro = []() { span s{ nullptr, static_cast::index_type>(0) }; }; CHECK_THROW(workaround_macro(), fail_fast); } @@ -168,10 +168,10 @@ SUITE(span_tests) } { - span s{nullptr, 0}; + span s{nullptr, static_cast::index_type>(0)}; CHECK(s.length() == 0 && s.data() == nullptr); - span cs{nullptr, 0}; + span cs{nullptr, static_cast::index_type>(0)}; CHECK(cs.length() == 0 && cs.data() == nullptr); } } @@ -194,7 +194,7 @@ SUITE(span_tests) { int* p = nullptr; - span s{p, 0}; + span s{p, static_cast::index_type>(0)}; CHECK(s.length() == 0 && s.data() == nullptr); } -- cgit v1.2.3 From b03b04bfcd436789724cb7dce8a40a9ae560c0a8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 20 Jul 2016 13:17:47 -0700 Subject: Applied clang-format to the source. --- .clang-format | 2 +- include/gsl.h | 107 ++++---- include/gsl_assert.h | 68 ++--- include/gsl_byte.h | 126 +++++---- include/gsl_util.h | 87 +++--- include/multi_span.h | 123 ++++----- include/span.h | 712 ++++++++++++++++++++++++++++---------------------- include/string_span.h | 530 +++++++++++++++++-------------------- 8 files changed, 913 insertions(+), 842 deletions(-) diff --git a/.clang-format b/.clang-format index b80d2c6..78696f5 100644 --- a/.clang-format +++ b/.clang-format @@ -16,6 +16,6 @@ AllowShortLoopsOnASingleLine: true PointerAlignment: Left AlignConsecutiveAssignments: false -AlignTrailingComments: false +AlignTrailingComments: true SpaceAfterCStyleCast: true diff --git a/include/gsl.h b/include/gsl.h index f4c9178..8e00a44 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -19,39 +19,38 @@ #ifndef GSL_GSL_H #define GSL_GSL_H -#include "gsl_assert.h" // Ensures/Expects -#include "gsl_util.h" // finally()/narrow()/narrow_cast()... -#include "span.h" // span -#include "multi_span.h" // multi_span, strided_span... -#include "string_span.h" // zstring, string_span, zstring_builder... +#include "gsl_assert.h" // Ensures/Expects +#include "gsl_util.h" // finally()/narrow()/narrow_cast()... +#include "multi_span.h" // multi_span, strided_span... +#include "span.h" // span +#include "string_span.h" // zstring, string_span, zstring_builder... #include #ifdef _MSC_VER // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr // MSVC 2013 workarounds #if _MSC_VER <= 1800 -// noexcept is not understood +// noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept // turn off some misguided warnings #pragma warning(push) -#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior #endif // _MSC_VER <= 1800 #endif // _MSC_VER - namespace gsl { // -// GSL.owner: ownership pointers +// GSL.owner: ownership pointers // using std::unique_ptr; using std::shared_ptr; @@ -59,67 +58,74 @@ using std::shared_ptr; template using owner = T; - // // not_null // // Restricts a pointer or smart pointer to only hold non-null values. -// +// // Has zero size overhead over T. // -// If T is a pointer (i.e. T == U*) then -// - allow construction from U* or U& +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& // - disallow construction from nullptr_t // - disallow default construction // - ensure construction from U* fails with nullptr // - allow implicit conversion to U* // -template +template class not_null { static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); + public: not_null(T t) : ptr_(t) { ensure_invariant(); } - not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } + not_null& operator=(const T& t) + { + ptr_ = t; + ensure_invariant(); + return *this; + } - not_null(const not_null &other) = default; - not_null& operator=(const not_null &other) = default; + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; template ::value>> - not_null(const not_null &other) + not_null(const not_null& other) { *this = other; } template ::value>> - not_null& operator=(const not_null &other) + not_null& operator=(const not_null& other) { ptr_ = other.get(); return *this; } - // prevents compilation when someone attempts to assign a nullptr + // prevents compilation when someone attempts to assign a nullptr not_null(std::nullptr_t) = delete; not_null(int) = delete; not_null& operator=(std::nullptr_t) = delete; - not_null& operator=(int) = delete; - - T get() const { + not_null& operator=(int) = delete; + + T get() const + { #ifdef _MSC_VER __assume(ptr_ != nullptr); #endif return ptr_; } // the assume() should help the optimizer - operator T() const { return get(); } + operator T() const { return get(); } T operator->() const { return get(); } - bool operator==(const T& rhs) const { return ptr_ == rhs; } - bool operator!=(const T& rhs) const { return !(*this == rhs); } + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } private: T ptr_; - // we assume that the compiler can hoist/prove away most of the checks inlined from this function + // we assume that the compiler can hoist/prove away most of the checks inlined from this + // function // if not, we could make them optional via conditional compilation void ensure_invariant() const { Expects(ptr_ != nullptr); } @@ -139,14 +145,11 @@ private: namespace std { - template - struct hash> - { - size_t operator()(const gsl::not_null & value) const - { - return hash{}(value); - } - }; +template +struct hash> +{ + size_t operator()(const gsl::not_null& value) const { return hash{}(value); } +}; } // namespace std @@ -159,7 +162,7 @@ namespace std #undef noexcept #pragma pop_macro("noexcept") - + #pragma warning(pop) #endif // _MSC_VER <= 1800 diff --git a/include/gsl_assert.h b/include/gsl_assert.h index 51e8ab6..10de31a 100644 --- a/include/gsl_assert.h +++ b/include/gsl_assert.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -28,50 +28,50 @@ // // 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 +// 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 +#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 // namespace gsl { -struct fail_fast : public std::runtime_error +struct fail_fast : public std::runtime_error { - explicit fail_fast(char const* const message) : std::runtime_error(message) {} + explicit fail_fast(char const* const message) : std::runtime_error(message) {} }; } #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) -#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__)); - +#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(); - +#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 +#define Expects(cond) +#define Ensures(cond) +#endif #endif // GSL_CONTRACTS_H diff --git a/include/gsl_byte.h b/include/gsl_byte.h index e2c79c9..5a9c327 100644 --- a/include/gsl_byte.h +++ b/include/gsl_byte.h @@ -1,17 +1,17 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// 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. -// +/////////////////////////////////////////////////////////////////////////////// +// +// 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 @@ -26,11 +26,11 @@ // constexpr is not understood #pragma push_macro("constexpr") -#define constexpr +#define constexpr -// noexcept is not understood +// noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept #endif // _MSC_VER <= 1800 @@ -38,54 +38,76 @@ namespace gsl { - // This is a simple definition for now that allows - // use of byte within span<> to be standards-compliant - enum class byte : unsigned char {}; +// This is a simple definition for now that allows +// use of byte within span<> to be standards-compliant +enum class byte : unsigned char +{ +}; + +template ::value>> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) << shift); +} - template ::value>> - constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept - { return b = byte(static_cast(b) << shift); } +template ::value>> +constexpr byte operator<<(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) >> shift); +} - template ::value>> - constexpr byte operator<<(byte b, IntegerType shift) noexcept - { return byte(static_cast(b) << shift); } +template ::value>> +constexpr byte operator>>(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) >> shift); +} - template ::value>> - constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept - { return b = byte(static_cast(b) >> shift); } +constexpr byte& operator|=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) | static_cast(r)); +} - template ::value>> - constexpr byte operator>> (byte b, IntegerType shift) noexcept - { return byte(static_cast(b) >> shift); } +constexpr byte operator|(byte l, byte r) noexcept +{ + return byte(static_cast(l) + static_cast(r)); +} - constexpr byte& operator|=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) | static_cast(r)); } +constexpr byte& operator&=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) & static_cast(r)); +} - constexpr byte operator|(byte l, byte r) noexcept - { return byte(static_cast(l) + static_cast(r)); } +constexpr byte operator&(byte l, byte r) noexcept +{ + return byte(static_cast(l) & static_cast(r)); +} - constexpr byte& operator&=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) & static_cast(r)); } +constexpr byte& operator^=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) ^ static_cast(r)); +} - constexpr byte operator&(byte l, byte r) noexcept - { return byte(static_cast(l) & static_cast(r)); } +constexpr byte operator^(byte l, byte r) noexcept +{ + return byte(static_cast(l) ^ static_cast(r)); +} - constexpr byte& operator^=(byte& l, byte r) noexcept - { return l = byte(static_cast(l) ^ static_cast(r)); } - - constexpr byte operator^(byte l, byte r) noexcept - { return byte(static_cast(l) ^ static_cast(r)); } - - constexpr byte operator~(byte b) noexcept - { return byte(~static_cast(b)); } +constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } - template ::value>> - constexpr IntegerType to_integer(byte b) noexcept { return {b}; } +template ::value>> +constexpr IntegerType to_integer(byte b) noexcept +{ + return {b}; +} - } // namespace gsl - #ifdef _MSC_VER #if _MSC_VER <= 1800 diff --git a/include/gsl_util.h b/include/gsl_util.h index d82937f..a3d5fbe 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -19,11 +19,11 @@ #ifndef GSL_UTIL_H #define GSL_UTIL_H -#include "gsl_assert.h" // Ensures/Expects +#include "gsl_assert.h" // Ensures/Expects #include -#include -#include #include +#include +#include #ifdef _MSC_VER @@ -32,7 +32,7 @@ #define constexpr #pragma warning(push) -#pragma warning(disable: 4127) // conditional expression is constant +#pragma warning(disable : 4127) // conditional expression is constant // MSVC 2013 workarounds #if _MSC_VER <= 1800 @@ -42,13 +42,12 @@ // turn off some misguided warnings #pragma warning(push) -#pragma warning(disable: 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior #endif // _MSC_VER <= 1800 #endif // _MSC_VER - namespace gsl { // @@ -60,18 +59,20 @@ template class final_act { public: - explicit final_act(F f) noexcept - : f_(std::move(f)), invoke_(true) - {} + 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(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_(); } + ~final_act() noexcept + { + if (invoke_) f_(); + } private: F f_; @@ -80,35 +81,44 @@ private: // finally() - convenience function to generate a final_act template -inline final_act finally(const F &f) -noexcept { return final_act(f); } +inline final_act finally(const F& f) noexcept +{ + return final_act(f); +} template -inline final_act finally(F &&f) noexcept -{ return final_act(std::forward(f)); } +inline final_act finally(F&& f) noexcept +{ + return final_act(std::forward(f)); +} // narrow_cast(): a searchable way to do narrowing casts of values -template +template inline constexpr T narrow_cast(U u) noexcept -{ return static_cast(u); } +{ + return static_cast(u); +} -struct narrowing_error : public std::exception {}; +struct narrowing_error : public std::exception +{ +}; namespace details { - template - struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> - {}; + template + struct is_same_signedness + : public std::integral_constant::value == std::is_signed::value> + { + }; } // narrow() : a checked version of narrow_cast() that throws if the cast changed the value -template +template inline T narrow(U u) { T t = narrow_cast(u); - if (static_cast(t) != u) - throw narrowing_error(); -#pragma warning(suppress:4127) // suppress warning from MSVC compiler about constant in if-test + if (static_cast(t) != u) throw narrowing_error(); +#pragma warning(suppress : 4127) // suppress warning from MSVC compiler about constant in if-test if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) throw narrowing_error(); return t; @@ -118,24 +128,35 @@ inline T narrow(U u) // at() - Bounds-checked way of accessing static arrays, std::array, std::vector // template -constexpr T& at(T(&arr)[N], size_t index) -{ Expects(index < N); return arr[index]; } +constexpr T& at(T (&arr)[N], size_t index) +{ + Expects(index < N); + return arr[index]; +} template constexpr T& at(std::array& arr, size_t index) -{ Expects(index < N); return arr[index]; } +{ + Expects(index < N); + return arr[index]; +} template constexpr typename Cont::value_type& at(Cont& cont, size_t index) -{ Expects(index < cont.size()); return cont[index]; } +{ + Expects(index < cont.size()); + return cont[index]; +} template constexpr const T& at(std::initializer_list cont, size_t index) -{ Expects(index < cont.size()); return *(cont.begin() + index); } +{ + Expects(index < cont.size()); + return *(cont.begin() + index); +} } // namespace gsl - #ifdef _MSC_VER #pragma warning(pop) diff --git a/include/multi_span.h b/include/multi_span.h index c4a0eac..a63f8ce 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -20,8 +20,8 @@ #define GSL_MULTI_SPAN_H #include "gsl_assert.h" -#include "gsl_util.h" #include "gsl_byte.h" +#include "gsl_util.h" #include #include #include @@ -52,7 +52,7 @@ #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG #define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT -// noexcept is not understood +// noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ @@ -217,10 +217,7 @@ public: return ret; } - friend constexpr index operator*(value_type v, const index& rhs) noexcept - { - return rhs * v; - } + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } constexpr index& operator*=(value_type v) noexcept { @@ -743,8 +740,7 @@ public: } constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept - : m_extents(extents), - m_strides(strides) + : m_extents(extents), m_strides(strides) { } @@ -841,8 +837,7 @@ public: using index_size_type = typename IndexType::value_type; template explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) + : boundary_(bnd.index_bounds()), curr_(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } @@ -930,7 +925,7 @@ public: return ret -= n; } - constexpr bounds_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { @@ -1137,7 +1132,7 @@ namespace details template std::enable_if_t< !std::is_same>::value && !std::is_same::value, T> - static_as_span_helper(Arg, Args... args) + static_as_span_helper(Arg, Args... args) { return static_as_span_helper(args...); } @@ -1159,7 +1154,8 @@ namespace details }; template - struct is_multi_span_oracle> : std::true_type + struct is_multi_span_oracle> + : std::true_type { }; @@ -1249,11 +1245,13 @@ public: constexpr multi_span(value_type&&) = delete; // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) {} + constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) + { + } // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), - bounds_(std::move(bounds)) + constexpr multi_span(pointer data, bounds_type bounds) noexcept + : data_(data), bounds_(std::move(bounds)) { Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); } @@ -1263,7 +1261,8 @@ public: typename = std::enable_if_t::value && details::LessThan::value>> constexpr multi_span(pointer begin, Ptr end) - : multi_span(begin, details::newBoundsHelper(static_cast(end) - begin)) + : multi_span(begin, + details::newBoundsHelper(static_cast(end) - begin)) { Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); } @@ -1273,9 +1272,8 @@ public: constexpr multi_span(T (&arr)[N]) : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) { - static_assert( - std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible::value, "Cannot construct a multi_span from an array with fewer elements."); } @@ -1286,17 +1284,17 @@ public: constexpr multi_span(T* const& data, size_type size) : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) { - static_assert( - std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); } // construct from std::array template - constexpr multi_span(std::array& arr) : multi_span(arr.data(), bounds_type{static_bounds{}}) + constexpr multi_span(std::array& arr) + : multi_span(arr.data(), bounds_type{static_bounds{}}) { static_assert( - std::is_convertible(*) []>::value, + std::is_convertible(*)[]>::value, "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible, bounds_type>::value, "You cannot construct a multi_span from a std::array of smaller size."); @@ -1307,7 +1305,7 @@ public: constexpr multi_span(const std::array, N>& arr) : multi_span(arr.data(), static_bounds()) { - static_assert(std::is_convertible>::value, + static_assert(std::is_convertible>::value, "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible, bounds_type>::value, "You cannot construct a multi_span from a std::array of smaller size."); @@ -1329,7 +1327,7 @@ public: DataType>::value>> constexpr multi_span(Cont& cont) : multi_span(static_cast(cont.data()), - details::newBoundsHelper(narrow_cast(cont.size()))) + details::newBoundsHelper(narrow_cast(cont.size()))) { } @@ -1348,8 +1346,8 @@ public: typename OtherBounds = static_bounds, typename = std::enable_if_t::value && std::is_convertible::value>> - constexpr multi_span(multi_span other) noexcept : data_(other.data_), - bounds_(other.bounds_) + constexpr multi_span(multi_span other) noexcept + : data_(other.data_), bounds_(other.bounds_) { } @@ -1424,7 +1422,8 @@ public: // subspan() - create a subview of count elements starting at offset // supplying dynamic_range for count will consume all available elements from offset constexpr multi_span subspan(size_type offset, - size_type count = dynamic_range) const noexcept + size_type count = dynamic_range) const + noexcept { Expects((offset >= 0 && offset <= this->size()) && (count == dynamic_range || (count <= this->size() - offset))); @@ -1535,7 +1534,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator==(const multi_span& other) const noexcept + constexpr bool operator==(const multi_span& other) const + noexcept { return bounds_.size() == other.bounds_.size() && (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); @@ -1544,7 +1544,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator!=(const multi_span& other) const noexcept + constexpr bool operator!=(const multi_span& other) const + noexcept { return !(*this == other); } @@ -1552,7 +1553,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator<(const multi_span& other) const noexcept + constexpr bool operator<(const multi_span& other) const + noexcept { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } @@ -1560,7 +1562,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator<=(const multi_span& other) const noexcept + constexpr bool operator<=(const multi_span& other) const + noexcept { return !(other < *this); } @@ -1568,7 +1571,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator>(const multi_span& other) const noexcept + constexpr bool operator>(const multi_span& other) const + noexcept { return (other < *this); } @@ -1576,7 +1580,8 @@ public: template , std::remove_cv_t>::value>> - constexpr bool operator>=(const multi_span& other) const noexcept + constexpr bool operator>=(const multi_span& other) const + noexcept { return !(*this < other); } @@ -1590,8 +1595,8 @@ public: // DimCount and Enabled here are workarounds for a bug in MSVC 2015 template 0), typename = std::enable_if_t> -constexpr multi_span as_span(SpanType s, - Dimensions2... dims) +constexpr multi_span +as_span(SpanType s, Dimensions2... dims) { static_assert(details::is_multi_span::value, "Variadic as_span() is for reshaping existing spans."); @@ -1628,13 +1633,13 @@ multi_span as_writeable_bytes(multi_span s) noexcept // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(multi_span s) noexcept - -> multi_span( - multi_span::bounds_type::static_size != dynamic_range - ? (static_cast( - multi_span::bounds_type::static_size) / - sizeof(U)) - : dynamic_range)> +constexpr auto as_span(multi_span s) noexcept -> multi_span< + const U, static_cast( + multi_span::bounds_type::static_size != dynamic_range + ? (static_cast( + multi_span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> { using ConstByteSpan = multi_span; static_assert( @@ -1653,12 +1658,13 @@ constexpr auto as_span(multi_span s) noexcept // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(multi_span s) noexcept -> multi_span< - U, narrow_cast( - multi_span::bounds_type::static_size != dynamic_range - ? static_cast(multi_span::bounds_type::static_size) / - sizeof(U) - : dynamic_range)> +constexpr auto as_span(multi_span s) noexcept + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range + ? static_cast( + multi_span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> { using ByteSpan = multi_span; static_assert( @@ -1816,9 +1822,9 @@ public: auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); size_type size = this->bounds().total_size() / d; - return {const_cast(reinterpret_cast(this->data())), size, - bounds_type{resize_extent(this->bounds().index_bounds(), d), - resize_stride(this->bounds().strides(), d)}}; + return {const_cast(reinterpret_cast(this->data())), + size, bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; } constexpr strided_span section(index_type origin, index_type extents) const @@ -2049,7 +2055,7 @@ public: contiguous_span_iterator ret{*this}; return ret -= n; } - contiguous_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } difference_type operator-(const contiguous_span_iterator& rhs) const noexcept { Expects(m_validator == rhs.m_validator); @@ -2148,16 +2154,13 @@ public: general_span_iterator ret{*this}; return ret -= n; } - general_span_iterator& operator-=(difference_type n) noexcept { return * this += -n; } + general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } difference_type operator-(const general_span_iterator& rhs) const noexcept { Expects(m_container == rhs.m_container); return m_itr - rhs.m_itr; } - value_type operator[](difference_type n) const noexcept - { - return (*m_container)[m_itr[n]]; - } + value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; } bool operator==(const general_span_iterator& rhs) const noexcept { diff --git a/include/span.h b/include/span.h index 0bffb4b..f40a7ab 100644 --- a/include/span.h +++ b/include/span.h @@ -20,11 +20,11 @@ #define GSL_SPAN_H #include "gsl_assert.h" -#include "gsl_util.h" #include "gsl_byte.h" +#include "gsl_util.h" #include -#include #include +#include #include #include #include @@ -39,7 +39,7 @@ // blanket turn off warnings from CppCoreCheck for now // so people aren't annoyed by them when running the tool. // more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable: 26481 26482 26483 26485 26490 26491 26492 26493 26495) +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) // No MSVC does constexpr fully yet #pragma push_macro("constexpr") @@ -51,7 +51,7 @@ #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG #define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT -// noexcept is not understood +// noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") #define noexcept /* nothing */ @@ -82,290 +82,340 @@ namespace gsl template class span; - -// [views.constants], constants +// [views.constants], constants constexpr const std::ptrdiff_t dynamic_extent = -1; - // implementation details namespace details { -template -struct is_span_oracle : std::false_type -{ -}; - -template -struct is_span_oracle> : std::true_type -{ -}; + template + struct is_span_oracle : std::false_type + { + }; -template -struct is_span : is_span_oracle> -{ -}; + template + struct is_span_oracle> : std::true_type + { + }; -template -struct is_allowed_pointer_conversion - : std::bool_constant< - std::is_pointer::value && - std::is_pointer::value && - std::is_convertible::value - > -{ -}; + template + struct is_span : is_span_oracle> + { + }; -template -struct is_allowed_integral_conversion - : std::bool_constant< - std::is_integral::value && - std::is_integral::value && - sizeof(From) == sizeof(To) && - alignof(From) == alignof(To) && - std::is_convertible::value - > -{ -}; + template + struct is_allowed_pointer_conversion + : std::bool_constant::value && std::is_pointer::value && + std::is_convertible::value> + { + }; -template -struct is_allowed_extent_conversion - : std::bool_constant< - From == To || - From == gsl::dynamic_extent || - To == gsl::dynamic_extent - > -{ -}; + template + struct is_allowed_integral_conversion + : std::bool_constant::value && std::is_integral::value && + sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && + std::is_convertible::value> + { + }; -template -struct is_allowed_element_type_conversion - : std::bool_constant< - std::is_same>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value - > -{ -}; + template + struct is_allowed_extent_conversion + : std::bool_constant + { + }; -template -struct is_allowed_element_type_conversion - : std::bool_constant::value> -{ -}; + template + struct is_allowed_element_type_conversion + : std::bool_constant>::value || + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value> + { + }; -template -struct is_allowed_element_type_conversion - : std::true_type -{ -}; + template + struct is_allowed_element_type_conversion + : std::bool_constant::value> + { + }; -template -class const_span_iterator -{ -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; + template + struct is_allowed_element_type_conversion : std::true_type + { + }; - using const_pointer = std::add_const_t; - using pointer = const_pointer; + template + class const_span_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; - using const_reference = std::add_const_t; - using reference = const_reference; + using const_pointer = std::add_const_t; + using pointer = const_pointer; - constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} - constexpr const_span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } + using const_reference = std::add_const_t; + using reference = const_reference; - constexpr reference operator*() const { Expects(span_); return (*span_)[index_]; } - constexpr pointer operator->() const { Expects(span_); return &((*span_)[index_]); } + constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} + constexpr const_span_iterator(const Span* span, typename Span::index_type index) + : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } - constexpr const_span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } + constexpr reference operator*() const + { + Expects(span_); + return (*span_)[index_]; + } + constexpr pointer operator->() const + { + Expects(span_); + return &((*span_)[index_]); + } - constexpr const_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } + constexpr const_span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } - constexpr const_span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } + constexpr const_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } - constexpr const_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } + constexpr const_span_iterator& operator--() noexcept + { + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; + return *this; + } - constexpr const_span_iterator operator+(difference_type n) const noexcept - { - auto ret{*this}; - return ret += n; - } + constexpr const_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } - constexpr const_span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } + constexpr const_span_iterator operator+(difference_type n) const noexcept + { + auto ret{*this}; + return ret += n; + } - constexpr const_span_iterator operator-(difference_type n) const noexcept - { - auto ret{*this}; - return ret -= n; - } + constexpr const_span_iterator& operator+=(difference_type n) noexcept + { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; + } - constexpr const_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + constexpr const_span_iterator operator-(difference_type n) const noexcept + { + auto ret{*this}; + return ret -= n; + } - constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } + constexpr const_span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } - constexpr reference operator[](difference_type n) const noexcept - { - return *(*this + n); - } + constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } - constexpr bool operator==(const const_span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - constexpr bool operator!=(const const_span_iterator& rhs) const noexcept { return !(*this == rhs); } + constexpr bool operator==(const const_span_iterator& rhs) const noexcept + { + return span_ == rhs.span_ && index_ == rhs.index_; + } - constexpr bool operator<(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } + constexpr bool operator!=(const const_span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } - constexpr bool operator<=(const const_span_iterator& rhs) const noexcept { return !(rhs < *this); } + constexpr bool operator<(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } - constexpr bool operator>(const const_span_iterator& rhs) const noexcept { return rhs < *this; } + constexpr bool operator<=(const const_span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } - constexpr bool operator>=(const const_span_iterator& rhs) const noexcept { return !(rhs > *this); } + constexpr bool operator>(const const_span_iterator& rhs) const noexcept + { + return rhs < *this; + } - void swap(const_span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(m_span, rhs.m_span); - } + constexpr bool operator>=(const const_span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } -private: - const Span* span_; - ptrdiff_t index_; -}; + void swap(const_span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(m_span, rhs.m_span); + } + private: + const Span* span_; + ptrdiff_t index_; + }; -template -class span_iterator : public const_span_iterator -{ - using base_type = const_span_iterator; + template + class span_iterator : public const_span_iterator + { + using base_type = const_span_iterator; -public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; - using pointer = value_type*; - using reference = value_type&; + using pointer = value_type*; + using reference = value_type&; - constexpr span_iterator() : base_type() {} - constexpr span_iterator(const Span* span, typename Span::index_type index) : base_type(span, index) {} + constexpr span_iterator() : base_type() {} + constexpr span_iterator(const Span* span, typename Span::index_type index) + : base_type(span, index) + { + } - constexpr reference operator*() const { return const_cast(base_type::operator*()); } - constexpr pointer operator->() const { return const_cast(base_type::operator->()); } + constexpr reference operator*() const + { + return const_cast(base_type::operator*()); + } + constexpr pointer operator->() const + { + return const_cast(base_type::operator->()); + } - constexpr span_iterator& operator++() noexcept { base_type::operator++(); return *this; } + constexpr span_iterator& operator++() noexcept + { + base_type::operator++(); + return *this; + } - constexpr span_iterator operator++(int) noexcept { return base_type::operator++(1); } + constexpr span_iterator operator++(int) noexcept { return base_type::operator++(1); } - constexpr span_iterator& operator--() noexcept { base_type::operator--(); return *this; } + constexpr span_iterator& operator--() noexcept + { + base_type::operator--(); + return *this; + } - constexpr span_iterator operator--(int) noexcept { return base_type::operator--(1); } + constexpr span_iterator operator--(int) noexcept { return base_type::operator--(1); } - constexpr span_iterator operator+(difference_type n) const noexcept { return base_type::operator+(n); } + constexpr span_iterator operator+(difference_type n) const noexcept + { + return base_type::operator+(n); + } - constexpr span_iterator& operator+=(difference_type n) noexcept { return base_type::operator+=(n); } + constexpr span_iterator& operator+=(difference_type n) noexcept + { + return base_type::operator+=(n); + } - constexpr span_iterator operator-(difference_type n) const noexcept { return base_type::operator-(n); } + constexpr span_iterator operator-(difference_type n) const noexcept + { + return base_type::operator-(n); + } - constexpr span_iterator& operator-=(difference_type n) noexcept { return base_type::operator-=(n); } + constexpr span_iterator& operator-=(difference_type n) noexcept + { + return base_type::operator-=(n); + } - constexpr difference_type operator-(const span_iterator& rhs) const noexcept { return base_type::operator-(rhs); } + constexpr difference_type operator-(const span_iterator& rhs) const noexcept + { + return base_type::operator-(rhs); + } - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - constexpr bool operator==(const span_iterator& rhs) const noexcept { return base_type::operator==(rhs); } + constexpr bool operator==(const span_iterator& rhs) const noexcept + { + return base_type::operator==(rhs); + } - constexpr bool operator!=(const span_iterator& rhs) const noexcept { return !(*this == rhs); } + constexpr bool operator!=(const span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } - constexpr bool operator<(const span_iterator& rhs) const noexcept { return base_type::operator<(rhs); } + constexpr bool operator<(const span_iterator& rhs) const noexcept + { + return base_type::operator<(rhs); + } - constexpr bool operator<=(const span_iterator& rhs) const noexcept { return !(rhs < *this); } + constexpr bool operator<=(const span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } - constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } - constexpr bool operator>=(const span_iterator& rhs) const noexcept { return !(rhs > *this); } + constexpr bool operator>=(const span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } - void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } -}; + void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } + }; -template -constexpr const_span_iterator operator+(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept -{ - return rhs + n; -} + template + constexpr const_span_iterator + operator+(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs + n; + } -template -constexpr const_span_iterator operator-(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept -{ - return rhs - n; -} + template + constexpr const_span_iterator + operator-(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs - n; + } -template -constexpr span_iterator operator+(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept -{ - return rhs + n; -} + template + constexpr span_iterator operator+(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs + n; + } -template -constexpr span_iterator operator-(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept -{ - return rhs - n; -} + template + constexpr span_iterator operator-(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs - n; + } } // namespace details - -// [span], class template span +// [span], class template span template -class span { +class span +{ public: - // constants and types + // constants and types using element_type = ElementType; using index_type = std::ptrdiff_t; using pointer = element_type*; @@ -378,135 +428,140 @@ public: constexpr static const index_type extent = Extent; - // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : storage_(nullptr, extent_type<0>()) - {} + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : storage_(nullptr, extent_type<0>()) {} - constexpr span(nullptr_t) noexcept : span() - {} + constexpr span(nullptr_t) noexcept : span() {} - constexpr span(pointer ptr, index_type count) : storage_(ptr, count) - {} + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} constexpr span(pointer firstElem, pointer lastElem) : storage_(firstElem, std::distance(firstElem, lastElem)) - {} - + { + } + template - constexpr span(element_type(&arr)[N]) noexcept - : storage_(&arr[0], extent_type()) - {} + constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], extent_type()) + { + } template - constexpr span(std::array& arr) noexcept - : storage_(&arr[0], extent_type()) - {} + constexpr span(std::array& arr) noexcept : storage_(&arr[0], extent_type()) + { + } template ::value>> constexpr span(std::array, N>& arr) noexcept : storage_(&arr[0], extent_type()) - {} + { + } template ::value>> constexpr span(const std::array, N>& arr) noexcept : storage_(&arr[0], extent_type()) - {} + { + } // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement // on Container to be a contiguous sequence container. template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value> - > - constexpr span(Container& cont) : span(cont.data(), cont.size()) {} + class = std::enable_if_t< + !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(Container& cont) : span(cont.data(), cont.size()) + { + } template ::value && - !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value> - > - constexpr span(const Container& cont) : span(cont.data(), cont.size()) {} + class = std::enable_if_t< + std::is_const::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(const Container& cont) : span(cont.data(), cont.size()) + { + } constexpr span(const span& other) noexcept = default; constexpr span(span&& other) noexcept = default; - template ::value && - details::is_allowed_element_type_conversion::value - > - > + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) : storage_(reinterpret_cast(other.data()), extent_type(other.size())) - {} + { + } - template ::value && - details::is_allowed_element_type_conversion::value - > - > + details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) : storage_(reinterpret_cast(other.data()), extent_type(other.size())) - {} + { + } ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; constexpr span& operator=(span&& other) noexcept = default; - // [span.sub], span subviews + // [span.sub], span subviews template constexpr span first() const { Expects(Count >= 0 && Count <= size()); - return { data(), Count }; + return {data(), Count}; } template constexpr span last() const { Expects(Count >= 0 && Count <= size()); - return{ data() + (size() - Count), Count }; + return {data() + (size() - Count), Count}; } template constexpr span subspan() const { Expects((Offset == 0 || Offset > 0 && Offset <= size()) && - (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())); - return { data() + Offset, Count == dynamic_extent ? size() - Offset : Count }; + (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())); + return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; } constexpr span first(index_type count) const { Expects(count >= 0 && count <= size()); - return { data(), count }; + return {data(), count}; } constexpr span last(index_type count) const { Expects(count >= 0 && count <= size()); - return { data() + (size() - count), count }; + return {data() + (size() - count), count}; } constexpr span subspan(index_type offset, - index_type count = dynamic_extent) const + index_type count = dynamic_extent) const { Expects((offset == 0 || offset > 0 && offset <= size()) && - (count == dynamic_extent || count >= 0 && offset + count <= size())); - return { data() + offset, count == dynamic_extent ? size() - offset : count }; + (count == dynamic_extent || count >= 0 && offset + count <= size())); + return {data() + offset, count == dynamic_extent ? size() - offset : count}; } - // [span.obs], span observers + // [span.obs], span observers constexpr index_type length() const noexcept { return size(); } - constexpr index_type size() const noexcept { return storage_.size(); } + constexpr index_type size() const noexcept { return storage_.size(); } constexpr index_type length_bytes() const noexcept { return size_bytes(); } constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } constexpr bool empty() const noexcept { return size() == 0; } - // [span.elem], span element access + // [span.elem], span element access constexpr reference operator[](index_type idx) const { Expects(idx >= 0 && idx < storage_.size()); @@ -515,13 +570,13 @@ public: constexpr reference operator()(index_type idx) const { return this->operator[](idx); } constexpr pointer data() const noexcept { return storage_.data(); } - // [span.iter], span iterator support + // [span.iter], span iterator support iterator begin() const noexcept { return {this, 0}; } iterator end() const noexcept { return {this, length()}; } const_iterator cbegin() const noexcept { return {this, 0}; } const_iterator cend() const noexcept { return {this, length()}; } - + reverse_iterator rbegin() const noexcept { return reverse_iterator{{this, length()}}; } reverse_iterator rend() const noexcept { return reverse_iterator{{this, 0}}; } @@ -531,7 +586,7 @@ public: private: template class extent_type; - + template class extent_type { @@ -544,12 +599,11 @@ private: constexpr extent_type(extent_type ext) noexcept { static_assert(Other == Extent || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); + "Mismatch between fixed-size extent and size of initializing data."); Expects(ext.size() == Extent); } - constexpr extent_type(index_type size) - { Expects(size == Extent); } + constexpr extent_type(index_type size) { Expects(size == Extent); } constexpr inline index_type size() const noexcept { return Extent; } }; @@ -560,19 +614,18 @@ private: public: template explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - {} + { + } - explicit constexpr extent_type(index_type size) : size_(size) - { Expects(size >= 0); } + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - constexpr inline index_type size() const noexcept - { return size_; } + constexpr inline index_type size() const noexcept { return size_; } private: index_type size_; }; - // this implementation detail class lets us take advantage of the + // this implementation detail class lets us take advantage of the // empty base class optimization to pay for only storage of a single // pointer in the case of fixed-size spans template @@ -580,12 +633,12 @@ private: { public: template - constexpr storage_type(pointer data, OtherExtentType ext) - : ExtentType(ext), data_(data) - { Expects((!data && size() == 0) || (data && size() >= 0)); } + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) + { + Expects((!data && size() == 0) || (data && size() >= 0)); + } - constexpr inline pointer data() const noexcept - { return data_; } + constexpr inline pointer data() const noexcept { return data_; } private: pointer data_; @@ -594,60 +647,81 @@ private: storage_type> storage_; }; - -// [span.comparison], span comparison operators +// [span.comparison], span comparison operators template -constexpr bool operator==(const span& l, const span& r) -{ return std::equal(l.begin(), l.end(), r.begin(), r.end()); } +constexpr bool operator==(const span& l, const span& r) +{ + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +} template -constexpr bool operator!=(const span& l, const span& r) -{ return !(l == r); } +constexpr bool operator!=(const span& l, const span& r) +{ + return !(l == r); +} template -constexpr bool operator<(const span& l, const span& r) -{ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } +constexpr bool operator<(const span& l, const span& r) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} template -constexpr bool operator<=(const span& l, const span& r) -{ return !(l > r); } +constexpr bool operator<=(const span& l, const span& r) +{ + return !(l > r); +} template -constexpr bool operator>(const span& l, const span& r) -{ return r < l; } +constexpr bool operator>(const span& l, const span& r) +{ + return r < l; +} template -constexpr bool operator>=(const span& l, const span& r) -{ return !(l < r); } - +constexpr bool operator>=(const span& l, const span& r) +{ + return !(l < r); +} namespace details { // if we only supported compilers with good constexpr support then // this pair of classes could collapse down to a constexpr function - // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as constexpr + // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as + // constexpr // and so will fail compilation of the template template - struct calculate_byte_size : - std::integral_constant(sizeof(ElementType) * static_cast(Extent))> - {}; + struct calculate_byte_size + : std::integral_constant(sizeof(ElementType) * + static_cast(Extent))> + { + }; template - struct calculate_byte_size : - std::integral_constant - {}; + struct calculate_byte_size + : std::integral_constant + { + }; } - -// [span.objectrep], views of object representation +// [span.objectrep], views of object representation template -span::value> as_bytes(span s) noexcept -{ return {reinterpret_cast(s.data()), s.size_bytes()}; } +span::value> +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} -template ::value>> -span::value> as_writeable_bytes(span s) noexcept -{ return {reinterpret_cast(s.data()), s.size_bytes()}; } +template ::value>> +span::value> +as_writeable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} } // namespace gsl diff --git a/include/string_span.h b/include/string_span.h index ccfe251..bef8288 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -36,8 +36,7 @@ // blanket turn off warnings from CppCoreCheck for now // so people aren't annoyed by them when running the tool. // more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable: 26481 26482 26483 26485 26490 26491 26492 26493 26495) - +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -80,19 +79,19 @@ namespace gsl // (sometimes needlessly) break existing programs when introduced. // -template +template using basic_zstring = CharT*; -template +template using czstring = basic_zstring; -template +template using cwzstring = basic_zstring; -template +template using zstring = basic_zstring; -template +template using wzstring = basic_zstring; // @@ -103,85 +102,93 @@ using wzstring = basic_zstring; // // Will fail-fast if sentinel cannot be found before max elements are examined. // -template +template span 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 }; + 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 -inline span ensure_z(T* const & sz, std::ptrdiff_t max = PTRDIFF_MAX) +template +inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) { return ensure_sentinel(sz, max); } -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const +// overloads to share an implementation inline span ensure_z(char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return {sz, static_cast(len)}; } inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { auto len = strnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return {sz, static_cast(len)}; } inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return {sz, static_cast(len)}; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { auto len = wcsnlen(sz, narrow_cast(max)); Ensures(sz[len] == 0); - return{ sz, static_cast(len) }; + return {sz, static_cast(len)}; } -template -span ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], static_cast(N)); } +template +span ensure_z(T (&sz)[N]) +{ + return ensure_z(&sz[0], static_cast(N)); +} -template -span::type, dynamic_extent> ensure_z(Cont& cont) +template +span::type, dynamic_extent> +ensure_z(Cont& cont) { return ensure_z(cont.data(), static_cast(cont.length())); } -template +template class basic_string_span; namespace details { template struct is_basic_string_span_oracle : std::false_type - {}; + { + }; template struct is_basic_string_span_oracle> : std::true_type - {}; + { + }; template struct is_basic_string_span : is_basic_string_span_oracle> - {}; + { + }; template struct length_func - {}; + { + }; template <> struct length_func @@ -220,7 +227,6 @@ namespace details }; } - // // string_span and relatives // @@ -247,19 +253,17 @@ public: // copy constexpr basic_string_span(const basic_string_span& other) = default; - // move +// move #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_string_span(basic_string_span&& other) = default; #else - constexpr basic_string_span(basic_string_span&& other) - : span_(std::move(other.span_)) - {} + constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} #endif // assign constexpr basic_string_span& operator=(const basic_string_span& other) = default; - // move assign +// move assign #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_string_span& operator=(basic_string_span&& other) = default; #else @@ -271,211 +275,160 @@ public: #endif // from nullptr - constexpr basic_string_span(std::nullptr_t ptr) noexcept - : span_(ptr) - {} + 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) - {} + 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 - constexpr basic_string_span(value_type(&arr)[N]) noexcept - : span_(remove_z(arr)) - {} + template + 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) - {} + 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(s.data()), narrow_cast(s.length())) - {} + { + } // from containers. Containers must have .size() and .data() function signatures template ::value - && !details::is_span::value - && std::is_convertible::value - && std::is_convertible().data())>::value> - > - constexpr basic_string_span(Cont& cont) : span_(cont.data(), cont.size()) {} + typename = std::enable_if_t< + !details::is_basic_string_span::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Cont& cont) : span_(cont.data(), cont.size()) + { + } // disallow creation from temporary containers and strings template ::value - && !details::is_span::value - && std::is_convertible::value - && std::is_convertible().data())>::value> - > - constexpr basic_string_span(const Cont& cont) : span_(cont.data(), cont.size()) {} + typename = std::enable_if_t< + !details::is_basic_string_span::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Cont& cont) : span_(cont.data(), cont.size()) + { + } #ifndef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE // from span template , impl_type>::value - > - > - constexpr basic_string_span(span other) noexcept - : span_(other) - {} + typename Dummy = std::enable_if_t< + std::is_convertible, impl_type>::value>> + constexpr basic_string_span(span other) noexcept : span_(other) + { + } #else // from span - constexpr basic_string_span(span other) noexcept - : span_(other) - {} - - template , value_type>::value>> + constexpr basic_string_span(span other) noexcept : span_(other) {} + + template , value_type>::value>> constexpr basic_string_span(span, Extent> other) noexcept : span_(other) - {} + { + } #endif // from string_span template ::impl_type, impl_type>::value - > - > + typename = std::enable_if_t::impl_type, impl_type>::value>> constexpr basic_string_span(basic_string_span other) noexcept : span_(other.data(), other.length()) - {} - - constexpr bool empty() const noexcept { - return length() == 0; } + constexpr bool empty() const noexcept { return length() == 0; } + // first Count elements - template + template constexpr basic_string_span first() const noexcept { - return{ span_.template first() }; + return {span_.template first()}; } constexpr basic_string_span first(size_type count) const noexcept { - return{ span_.first(count) }; + return {span_.first(count)}; } // last Count elements - template + template constexpr basic_string_span last() const noexcept { - return{ span_.template last() }; + return {span_.template last()}; } constexpr basic_string_span last(size_type count) const noexcept { - return{ span_.last(count) }; + return {span_.last(count)}; } // create a subview of Count elements starting from Offset - template + template constexpr basic_string_span subspan() const noexcept { - return{ span_.template subspan() }; + return {span_.template subspan()}; } - constexpr basic_string_span subspan(size_type offset, size_type count = dynamic_extent) const noexcept + constexpr basic_string_span + subspan(size_type offset, size_type count = dynamic_extent) const noexcept { - return{ span_.subspan(offset, count) }; + return {span_.subspan(offset, count)}; } - constexpr reference operator[](size_type idx) const noexcept - { - return span_[idx]; - } + constexpr reference operator[](size_type idx) const noexcept { return span_[idx]; } - constexpr pointer data() const noexcept - { - return span_.data(); - } + constexpr pointer data() const noexcept { return span_.data(); } // length of the span in elements - constexpr size_type length() const noexcept - { - return span_.size(); - } + constexpr size_type length() const noexcept { return span_.size(); } // length of the span in elements - constexpr size_type size() const noexcept - { - return span_.size(); - } + 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(); - } + 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 size_type length_bytes() const noexcept { return span_.length_bytes(); } - constexpr iterator begin() const noexcept - { - return span_.begin(); - } + constexpr iterator begin() const noexcept { return span_.begin(); } - constexpr iterator end() const noexcept - { - return span_.end(); - } + constexpr iterator end() const noexcept { return span_.end(); } - constexpr const_iterator cbegin() const noexcept - { - return span_.cbegin(); - } + constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); } - constexpr const_iterator cend() const noexcept - { - return span_.cend(); - } + constexpr const_iterator cend() const noexcept { return span_.cend(); } - constexpr reverse_iterator rbegin() const noexcept - { - return span_.rbegin(); - } + constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); } - constexpr reverse_iterator rend() const noexcept - { - return span_.rend(); - } + constexpr reverse_iterator rend() const noexcept { return span_.rend(); } - constexpr const_reverse_iterator crbegin() const noexcept - { - return span_.crbegin(); - } + constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); } - constexpr const_reverse_iterator crend() const noexcept - { - return span_.crend(); - } + 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()(sz, max)}; + return {sz, details::length_func()(sz, max)}; } - template - static impl_type remove_z(value_type(&sz)[N]) noexcept + template + static impl_type remove_z(value_type (&sz)[N]) noexcept { return remove_z(&sz[0], narrow_cast(N)); } @@ -483,16 +436,16 @@ private: impl_type span_; }; -template +template using string_span = basic_string_span; -template +template using cstring_span = basic_string_span; -template +template using wstring_span = basic_string_span; -template +template using cwstring_span = basic_string_span; // @@ -500,39 +453,40 @@ using cwstring_span = basic_string_span; // #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -template -std::basic_string::type> to_string(basic_string_span view) +template +std::basic_string::type> +to_string(basic_string_span view) { - return{ view.data(), static_cast(view.length()) }; + return {view.data(), static_cast(view.length())}; } #else inline std::string to_string(cstring_span<> view) { - return{ view.data(), static_cast(view.length()) }; + return {view.data(), static_cast(view.length())}; } inline std::string to_string(string_span<> view) { - return{ view.data(), static_cast(view.length()) }; + return {view.data(), static_cast(view.length())}; } inline std::wstring to_string(cwstring_span<> view) { - return{ view.data(), static_cast(view.length()) }; + return {view.data(), static_cast(view.length())}; } inline std::wstring to_string(wstring_span<> view) { - return{ view.data(), static_cast(view.length()) }; + return {view.data(), static_cast(view.length())}; } #endif // zero-terminated string span, used to convert // zero-terminated spans to legacy strings -template +template class basic_zstring_span { public: @@ -548,8 +502,7 @@ public: using impl_type = span; using string_span_type = basic_string_span; - constexpr basic_zstring_span(impl_type s) noexcept - : span_(s) + constexpr basic_zstring_span(impl_type s) noexcept : span_(s) { // expects a zero-terminated span Expects(s[s.size() - 1] == '\0'); @@ -558,19 +511,17 @@ public: // copy constexpr basic_zstring_span(const basic_zstring_span& other) = default; - // move +// move #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_zstring_span(basic_zstring_span&& other) = default; #else - constexpr basic_zstring_span(basic_zstring_span&& other) - : span_(std::move(other.span_)) - {} + constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {} #endif // assign constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; - // move assign +// move assign #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; #else @@ -583,7 +534,10 @@ public: constexpr bool empty() const noexcept { return span_.size() == 0; } - constexpr string_span_type as_string_span() const noexcept { return span_.first(span_.size()-1); } + constexpr string_span_type as_string_span() const noexcept + { + return span_.first(span_.size() - 1); + } constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } @@ -609,9 +563,8 @@ using cwzstring_span = basic_zstring_span; // operator == template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator==(gsl::basic_string_span one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); @@ -622,11 +575,11 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc #endif } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator==(const T& one, gsl::basic_string_span other) noexcept { gsl::basic_string_span, Extent> tmp(one); @@ -637,33 +590,33 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #endif } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator==(gsl::basic_string_span one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator==(const T& one, gsl::basic_string_span other) noexcept { gsl::basic_string_span, Extent> tmp(one); @@ -673,50 +626,49 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc // operator != template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator!=(gsl::basic_string_span one, const T& other) noexcept { return !(one == other); } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator!=(const T& one, gsl::basic_string_span other) noexcept { return !(one == other); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator!=(gsl::basic_string_span one, const T& other) noexcept { return !(one == other); } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator!=(const T& one, gsl::basic_string_span other) noexcept { return !(one == other); @@ -725,53 +677,52 @@ bool operator!=(const T& one, gsl::basic_string_span other) noexc // operator< template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator<(gsl::basic_string_span one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator<(const T& one, gsl::basic_string_span other) noexcept { gsl::basic_string_span, Extent> tmp(one); return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator<(gsl::basic_string_span one, const T& other) noexcept { gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator<(const T& one, gsl::basic_string_span other) noexcept { gsl::basic_string_span, Extent> tmp(one); @@ -781,50 +732,49 @@ bool operator<(const T& one, gsl::basic_string_span other) noexce // operator <= template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator<=(gsl::basic_string_span one, const T& other) noexcept { return !(other < one); } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator<=(const T& one, gsl::basic_string_span other) noexcept { return !(other < one); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator<=(gsl::basic_string_span one, const T& other) noexcept { return !(other < one); } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator<=(const T& one, gsl::basic_string_span other) noexcept { return !(other < one); @@ -833,50 +783,49 @@ bool operator<=(const T& one, gsl::basic_string_span other) noexc // operator> template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator>(gsl::basic_string_span one, const T& other) noexcept { return other < one; } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator>(const T& one, gsl::basic_string_span other) noexcept { return other < one; } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator>(gsl::basic_string_span one, const T& other) noexcept { return other < one; } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator>(const T& one, gsl::basic_string_span other) noexcept { return other < one; @@ -885,50 +834,49 @@ bool operator>(const T& one, gsl::basic_string_span other) noexce // operator >= template , Extent>>::value> -> + typename = std::enable_if_t, Extent>>::value>> bool operator>=(gsl::basic_string_span one, const T& other) noexcept { return !(one < other); } -template , Extent>>::value - && !gsl::details::is_basic_string_span::value> -> + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); } -#ifndef _MSC_VER +#ifndef _MSC_VER // VS treats temp and const containers as convertible to basic_string_span, // so the cases below are already covered by the previous operators -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator>=(gsl::basic_string_span one, const T& other) noexcept { return !(one < other); } -template ::value - && !gsl::details::is_basic_string_span::value - && std::is_convertible::value - && std::is_same().size(), *std::declval().data())>, DataType>::value> -> + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> bool operator>=(const T& one, gsl::basic_string_span other) noexcept { return !(one < other); -- cgit v1.2.3 From c366f4415d581b03e2a900cf36c40e830cd05bf9 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 26 Jul 2016 18:34:27 -0700 Subject: Fixed compilation issues for GCC on Linux. --- include/gsl_util.h | 1 - include/span.h | 205 +++++++++++++++++++---------------- include/string_span.h | 258 ++++++++++++++++---------------------------- tests/string_span_tests.cpp | 6 +- 4 files changed, 206 insertions(+), 264 deletions(-) diff --git a/include/gsl_util.h b/include/gsl_util.h index a3d5fbe..92a795b 100644 --- a/include/gsl_util.h +++ b/include/gsl_util.h @@ -118,7 +118,6 @@ inline T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); -#pragma warning(suppress : 4127) // suppress warning from MSVC compiler about constant in if-test if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) throw narrowing_error(); return t; diff --git a/include/span.h b/include/span.h index f40a7ab..27c479d 100644 --- a/include/span.h +++ b/include/span.h @@ -79,12 +79,12 @@ namespace gsl { -template -class span; - // [views.constants], constants constexpr const std::ptrdiff_t dynamic_extent = -1; +template +class span; + // implementation details namespace details { @@ -99,20 +99,35 @@ namespace details }; template - struct is_span : is_span_oracle> + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : public is_std_array_oracle> { }; template struct is_allowed_pointer_conversion - : std::bool_constant::value && std::is_pointer::value && + : public std::integral_constant::value && std::is_pointer::value && std::is_convertible::value> { }; template struct is_allowed_integral_conversion - : std::bool_constant::value && std::is_integral::value && + : public std::integral_constant::value && std::is_integral::value && sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && std::is_convertible::value> { @@ -120,13 +135,13 @@ namespace details template struct is_allowed_extent_conversion - : std::bool_constant + : public std::integral_constant { }; template struct is_allowed_element_type_conversion - : std::bool_constant>::value || + : public std::integral_constant>::value || is_allowed_pointer_conversion::value || is_allowed_integral_conversion::value> { @@ -134,12 +149,12 @@ namespace details template struct is_allowed_element_type_conversion - : std::bool_constant::value> + : public std::integral_constant::value> { }; template - struct is_allowed_element_type_conversion : std::true_type + struct is_allowed_element_type_conversion : public std::true_type { }; @@ -269,12 +284,12 @@ namespace details void swap(const_span_iterator& rhs) noexcept { std::swap(index_, rhs.index_); - std::swap(m_span, rhs.m_span); + std::swap(span_, rhs.span_); } private: const Span* span_; - ptrdiff_t index_; + std::ptrdiff_t index_; }; template @@ -323,12 +338,12 @@ namespace details constexpr span_iterator operator+(difference_type n) const noexcept { - return base_type::operator+(n); + return {base_type::operator+(n)}; } constexpr span_iterator& operator+=(difference_type n) noexcept { - return base_type::operator+=(n); + return {base_type::operator+=(n)}; } constexpr span_iterator operator-(difference_type n) const noexcept @@ -376,6 +391,10 @@ namespace details } void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } + private: + constexpr span_iterator(const base_type& base) : base_type(base) + { + } }; template @@ -408,6 +427,47 @@ namespace details return rhs - n; } + template + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type ext) noexcept + { + static_assert(Other == Ext || Other == dynamic_extent, + "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Ext); + } + + constexpr extent_type(index_type size) { Expects(size == Ext); } + + constexpr inline index_type size() const noexcept { return Ext; } + }; + + template <> + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + { + } + + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept { return size_; } + + private: + index_type size_; + }; } // namespace details // [span], class template span @@ -429,9 +489,9 @@ public: constexpr static const index_type extent = Extent; // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : storage_(nullptr, extent_type<0>()) {} + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} - constexpr span(nullptr_t) noexcept : span() {} + constexpr span(std::nullptr_t) noexcept : span() {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} @@ -441,24 +501,19 @@ public: } template - constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], extent_type()) + constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) { } - - template - constexpr span(std::array& arr) noexcept : storage_(&arr[0], extent_type()) + + template > + constexpr span(std::array& arr) noexcept + : storage_(&arr[0], details::extent_type()) { } - template ::value>> - constexpr span(std::array, N>& arr) noexcept - : storage_(&arr[0], extent_type()) - { - } - - template ::value>> + template constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], extent_type()) + : storage_(&arr[0], details::extent_type()) { } @@ -467,8 +522,9 @@ public: template ::value && - std::is_convertible::value && - std::is_convertible::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> constexpr span(Container& cont) : span(cont.data(), cont.size()) { @@ -477,8 +533,8 @@ public: template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible::value && + std::is_convertible().data())>::value>> constexpr span(const Container& cont) : span(cont.data(), cont.size()) { @@ -493,7 +549,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) { } @@ -503,7 +559,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) { } @@ -512,25 +568,25 @@ public: constexpr span& operator=(span&& other) noexcept = default; // [span.sub], span subviews - template + template constexpr span first() const { Expects(Count >= 0 && Count <= size()); return {data(), Count}; } - template + template constexpr span last() const { Expects(Count >= 0 && Count <= size()); return {data() + (size() - Count), Count}; } - template + template constexpr span subspan() const { - Expects((Offset == 0 || Offset > 0 && Offset <= size()) && - (Count == dynamic_extent || Count >= 0 && Offset + Count <= size())); + Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && + (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; } @@ -549,8 +605,8 @@ public: constexpr span subspan(index_type offset, index_type count = dynamic_extent) const { - Expects((offset == 0 || offset > 0 && offset <= size()) && - (count == dynamic_extent || count >= 0 && offset + count <= size())); + Expects((offset == 0 || (offset > 0 && offset <= size())) && + (count == dynamic_extent || (count >= 0 && offset + count <= size()))); return {data() + offset, count == dynamic_extent ? size() - offset : count}; } @@ -584,47 +640,6 @@ public: const_reverse_iterator crend() const noexcept { return reverse_iterator{{this, 0}}; } private: - template - class extent_type; - - template - class extent_type - { - public: - static_assert(Extent >= 0, "A fixed-size span must be >= 0 in size."); - - constexpr extent_type() noexcept {} - - template - constexpr extent_type(extent_type ext) noexcept - { - static_assert(Other == Extent || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); - Expects(ext.size() == Extent); - } - - constexpr extent_type(index_type size) { Expects(size == Extent); } - - constexpr inline index_type size() const noexcept { return Extent; } - }; - - template <> - class extent_type - { - public: - template - explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } - - explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - - constexpr inline index_type size() const noexcept { return size_; } - - private: - index_type size_; - }; - // this implementation detail class lets us take advantage of the // empty base class optimization to pay for only storage of a single // pointer in the case of fixed-size spans @@ -644,41 +659,41 @@ private: pointer data_; }; - storage_type> storage_; + storage_type> storage_; }; // [span.comparison], span comparison operators -template -constexpr bool operator==(const span& l, const span& r) +template +constexpr bool operator==(const span& l, const span& r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); } -template +template constexpr bool operator!=(const span& l, const span& r) { return !(l == r); } -template +template constexpr bool operator<(const span& l, const span& r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } -template +template constexpr bool operator<=(const span& l, const span& r) { return !(l > r); } -template +template constexpr bool operator>(const span& l, const span& r) { return r < l; } -template +template constexpr bool operator>=(const span& l, const span& r) { return !(l < r); @@ -692,11 +707,11 @@ namespace details // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as // constexpr // and so will fail compilation of the template - template + template struct calculate_byte_size : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> + static_cast(sizeof(ElementType) * + static_cast(Extent))> { }; @@ -708,14 +723,14 @@ namespace details } // [span.objectrep], views of object representation -template +template span::value> as_bytes(span s) noexcept { return {reinterpret_cast(s.data()), s.size_bytes()}; } -template ::value>> span::value> as_writeable_bytes(span s) noexcept diff --git a/include/string_span.h b/include/string_span.h index bef8288..723f650 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -234,40 +234,39 @@ template class basic_string_span { public: - using value_type = CharT; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t; - using impl_type = span; + using element_type = CharT; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + using impl_type = span; - using size_type = ptrdiff_t; + using index_type = typename impl_type::index_type; 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; + constexpr basic_string_span() noexcept = default; // copy - constexpr basic_string_span(const basic_string_span& other) = default; + constexpr basic_string_span(const basic_string_span& other) noexcept = default; // move #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span(basic_string_span&& other) = default; + constexpr basic_string_span(basic_string_span&& other) noexcept = default; #else constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} #endif // assign - constexpr basic_string_span& operator=(const basic_string_span& other) = default; + constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; // move assign #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span& operator=(basic_string_span&& other) = default; + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; #else - constexpr basic_string_span& operator=(basic_string_span&& other) + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept { span_ = std::move(other.span_); return *this; @@ -277,47 +276,55 @@ public: // 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) - { - } + constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} + constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} // From static arrays - if 0-terminated, remove 0 from the view - - // from static arrays and string literals + // All other containers allow 0s within the length, so we do not remove them template - constexpr basic_string_span(value_type (&arr)[N]) noexcept : span_(remove_z(arr)) + constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) + { + } + + template > + constexpr basic_string_span(std::array& arr) noexcept + : span_(arr) {} + + template > + constexpr basic_string_span(const std::array& arr) noexcept + : span_(arr) {} + + // Container signature should work for basic_string after C++17 version exists + template + constexpr basic_string_span(std::basic_string& str) + : span_(&str[0], str.length()) { } - // 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(s.data()), narrow_cast(s.length())) + template + constexpr basic_string_span(const std::basic_string& str) + : span_(&str[0], str.length()) { } - // from containers. Containers must have .size() and .data() function signatures - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(Cont& cont) : span_(cont.data(), cont.size()) + // from containers. Containers must have a pointer type and data() function signatures + template ::value && + !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Container& cont) : span_(cont) { } - // disallow creation from temporary containers and strings - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(const Cont& cont) : span_(cont.data(), cont.size()) + template ::value && + !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Container& cont) : span_(cont) { } @@ -326,109 +333,97 @@ public: template , impl_type>::value>> - constexpr basic_string_span(span other) noexcept : span_(other) + constexpr basic_string_span(const span& other) : span_(other) { } #else // from span - constexpr basic_string_span(span other) noexcept : span_(other) {} + constexpr basic_string_span(span other) : span_(other) {} template , value_type>::value>> - constexpr basic_string_span(span, Extent> other) noexcept + !std::is_same, value_type>::value>> + constexpr basic_string_span(const span, Extent>& other) : span_(other) { } #endif // from string_span - template ::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) noexcept + template ::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) : span_(other.data(), other.length()) { } - constexpr bool empty() const noexcept { return length() == 0; } - // first Count elements - template - constexpr basic_string_span first() const noexcept + template + constexpr basic_string_span first() const { return {span_.template first()}; } - constexpr basic_string_span first(size_type count) const noexcept + constexpr basic_string_span first(index_type count) const { return {span_.first(count)}; } // last Count elements - template - constexpr basic_string_span last() const noexcept + template + constexpr basic_string_span last() const { return {span_.template last()}; } - constexpr basic_string_span last(size_type count) const noexcept + constexpr basic_string_span last(index_type count) const { return {span_.last(count)}; } - // create a subview of Count elements starting from Offset - template - constexpr basic_string_span subspan() const noexcept + template + constexpr basic_string_span subspan() const { return {span_.template subspan()}; } - constexpr basic_string_span - subspan(size_type offset, size_type count = dynamic_extent) const noexcept + constexpr basic_string_span + subspan(index_type offset, index_type count = dynamic_extent) const { return {span_.subspan(offset, count)}; } - constexpr reference operator[](size_type idx) const noexcept { return span_[idx]; } - - constexpr pointer data() const noexcept { return span_.data(); } + constexpr reference operator[](index_type idx) const { return span_[idx]; } + constexpr reference operator()(index_type idx) const { return span_[idx]; } - // length of the span in elements - constexpr size_type length() const noexcept { return span_.size(); } + constexpr pointer data() const { return span_.data(); } - // 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 index_type length() const noexcept { return span_.size(); } + constexpr index_type size() const noexcept { return span_.size(); } + constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } + constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr bool empty() const noexcept { return size() == 0; } 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 { return 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 + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) { - return {sz, details::length_func()(sz, max)}; + return {sz, details::length_func()(sz, max)}; } template - static impl_type remove_z(value_type (&sz)[N]) noexcept + static impl_type remove_z(element_type (&sz)[N]) { return remove_z(&sz[0], narrow_cast(N)); } @@ -453,7 +448,7 @@ using cwstring_span = basic_string_span; // #ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -template +template std::basic_string::type> to_string(basic_string_span view) { @@ -559,15 +554,13 @@ using czstring_span = basic_zstring_span; template using cwzstring_span = basic_zstring_span; -} // namespace GSL - // operator == -template , Extent>>::value>> -bool operator==(gsl::basic_string_span one, const T& other) noexcept +template ::value || std::is_convertible< + T, gsl::basic_string_span>>::value>> +bool operator==(const gsl::basic_string_span& one, const T& other) noexcept { - gsl::basic_string_span, Extent> tmp(other); + gsl::basic_string_span> tmp(other); #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); #else @@ -576,13 +569,13 @@ bool operator==(gsl::basic_string_span one, const T& other) noexc } template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator==(const T& one, gsl::basic_string_span other) noexcept + class CharT, std::ptrdiff_t Extent, class T, + class = std::enable_if_t::value && + std::is_convertible>>::value + >> +bool operator==(const T& one, const gsl::basic_string_span& other) noexcept { - gsl::basic_string_span, Extent> tmp(one); + gsl::basic_string_span> tmp(one); #ifdef GSL_MSVC_NO_CPP14_STD_EQUAL return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); #else @@ -590,40 +583,6 @@ bool operator==(const T& one, gsl::basic_string_span other) noexc #endif } -#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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator==(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator==(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, Extent> tmp(one); - return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); -} -#endif - // operator != template other) noexc 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one == other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator!=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one == other); -} -#endif - // operator< template =(const T& one, gsl::basic_string_span other) noexc return !(one < other); } #endif +} // namespace GSL #ifdef _MSC_VER diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index f380be3..876886a 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -40,14 +40,14 @@ SUITE(string_span_tests) { std::string s = "Hello there world"; cstring_span<> v = s; - CHECK(v.length() == static_cast::size_type>(s.length())); + CHECK(v.length() == static_cast::index_type>(s.length())); } TEST(TestConstructFromStdVector) { std::vector vec(5, 'h'); string_span<> v {vec}; - CHECK(v.length() == static_cast::size_type>(vec.size())); + CHECK(v.length() == static_cast::index_type>(vec.size())); } TEST(TestStackArrayConstruction) @@ -109,7 +109,7 @@ SUITE(string_span_tests) char stack_string[] = "Hello"; cstring_span<> v = ensure_z(stack_string); auto s2 = gsl::to_string(v); - CHECK(static_cast::size_type>(s2.length()) == v.length()); + CHECK(static_cast::index_type>(s2.length()) == v.length()); CHECK(s2.length() == 5); } -- cgit v1.2.3 From 4de3d4e3e314016ee910a21d7c040e343c9871fb Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 26 Jul 2016 18:44:13 -0700 Subject: Fixes for Clang 3.6 compilation. --- include/span.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/span.h b/include/span.h index 27c479d..decc8e2 100644 --- a/include/span.h +++ b/include/span.h @@ -220,7 +220,7 @@ namespace details constexpr const_span_iterator operator+(difference_type n) const noexcept { - auto ret{*this}; + auto ret = *this; return ret += n; } @@ -233,7 +233,7 @@ namespace details constexpr const_span_iterator operator-(difference_type n) const noexcept { - auto ret{*this}; + auto ret = *this; return ret -= n; } @@ -650,7 +650,7 @@ private: template constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) { - Expects((!data && size() == 0) || (data && size() >= 0)); + Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); } constexpr inline pointer data() const noexcept { return data_; } -- cgit v1.2.3 From 6fadce975b95952f13ffafdcee88795222b711f3 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 26 Jul 2016 19:19:47 -0700 Subject: Applied clang-format. --- include/multi_span.h | 15 +++++++----- include/span.h | 43 ++++++++++++++++++--------------- include/string_span.h | 67 +++++++++++++++++++++++++++------------------------ 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index a63f8ce..cfe2d5c 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -740,7 +740,8 @@ public: } constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept - : m_extents(extents), m_strides(strides) + : m_extents(extents), + m_strides(strides) { } @@ -837,7 +838,8 @@ public: using index_size_type = typename IndexType::value_type; template explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary_(bnd.index_bounds()), curr_(std::move(curr)) + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } @@ -1132,7 +1134,7 @@ namespace details template std::enable_if_t< !std::is_same>::value && !std::is_same::value, T> - static_as_span_helper(Arg, Args... args) + static_as_span_helper(Arg, Args... args) { return static_as_span_helper(args...); } @@ -1250,8 +1252,8 @@ public: } // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) noexcept - : data_(data), bounds_(std::move(bounds)) + constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) { Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); } @@ -1347,7 +1349,8 @@ public: typename = std::enable_if_t::value && std::is_convertible::value>> constexpr multi_span(multi_span other) noexcept - : data_(other.data_), bounds_(other.bounds_) + : data_(other.data_), + bounds_(other.bounds_) { } diff --git a/include/span.h b/include/span.h index decc8e2..4d8e877 100644 --- a/include/span.h +++ b/include/span.h @@ -120,30 +120,33 @@ namespace details template struct is_allowed_pointer_conversion - : public std::integral_constant::value && std::is_pointer::value && - std::is_convertible::value> + : public std::integral_constant::value && + std::is_pointer::value && + std::is_convertible::value> { }; template struct is_allowed_integral_conversion - : public std::integral_constant::value && std::is_integral::value && - sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && - std::is_convertible::value> + : public std::integral_constant< + bool, std::is_integral::value && std::is_integral::value && + sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && + std::is_convertible::value> { }; template struct is_allowed_extent_conversion - : public std::integral_constant + : public std::integral_constant { }; template struct is_allowed_element_type_conversion : public std::integral_constant>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value> + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value> { }; @@ -392,9 +395,7 @@ namespace details void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } private: - constexpr span_iterator(const base_type& base) : base_type(base) - { - } + constexpr span_iterator(const base_type& base) : base_type(base) {} }; template @@ -431,7 +432,7 @@ namespace details class extent_type { public: - using index_type = std::ptrdiff_t; + using index_type = std::ptrdiff_t; static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); @@ -454,7 +455,7 @@ namespace details class extent_type { public: - using index_type = std::ptrdiff_t; + using index_type = std::ptrdiff_t; template explicit constexpr extent_type(extent_type ext) : size_(ext.size()) @@ -504,7 +505,7 @@ public: constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) { } - + template > constexpr span(std::array& arr) noexcept : storage_(&arr[0], details::extent_type()) @@ -521,8 +522,7 @@ public: // on Container to be a contiguous sequence container. template ::value && - !details::is_std_array::value && + !details::is_span::value && !details::is_std_array::value && std::is_convertible::value && std::is_convertible().data())>::value>> @@ -549,7 +549,8 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) { } @@ -559,7 +560,8 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), details::extent_type(other.size())) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) { } @@ -664,7 +666,8 @@ private: // [span.comparison], span comparison operators template -constexpr bool operator==(const span& l, const span& r) +constexpr bool operator==(const span& l, + const span& r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); } @@ -711,7 +714,7 @@ namespace details struct calculate_byte_size : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> + static_cast(Extent))> { }; diff --git a/include/string_span.h b/include/string_span.h index 723f650..1df7e6e 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -285,25 +285,27 @@ public: constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) { } - + template > - constexpr basic_string_span(std::array& arr) noexcept - : span_(arr) {} + constexpr basic_string_span(std::array& arr) noexcept : span_(arr) + { + } template > - constexpr basic_string_span(const std::array& arr) noexcept - : span_(arr) {} + constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) + { + } // Container signature should work for basic_string after C++17 version exists template constexpr basic_string_span(std::basic_string& str) - : span_(&str[0], str.length()) + : span_(&str[0], str.length()) { } template constexpr basic_string_span(const std::basic_string& str) - : span_(&str[0], str.length()) + : span_(&str[0], str.length()) { } @@ -313,7 +315,8 @@ public: !details::is_basic_string_span::value && !details::is_span::value && std::is_convertible::value && - std::is_convertible().data())>::value>> + std::is_convertible().data())>::value>> constexpr basic_string_span(Container& cont) : span_(cont) { } @@ -323,7 +326,8 @@ public: !details::is_basic_string_span::value && !details::is_span::value && std::is_convertible::value && - std::is_convertible().data())>::value>> + std::is_convertible().data())>::value>> constexpr basic_string_span(const Container& cont) : span_(cont) { } @@ -342,53 +346,54 @@ public: template , value_type>::value>> - constexpr basic_string_span(const span, Extent>& other) + constexpr basic_string_span(const span, Extent>& other) : span_(other) { } #endif // from string_span - template ::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) + template < + class OtherValueType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) : span_(other.data(), other.length()) { } // first Count elements template - constexpr basic_string_span first() const + constexpr basic_string_span first() const { return {span_.template first()}; } - constexpr basic_string_span first(index_type count) const + constexpr basic_string_span first(index_type count) const { return {span_.first(count)}; } // last Count elements template - constexpr basic_string_span last() const + constexpr basic_string_span last() const { return {span_.template last()}; } - constexpr basic_string_span last(index_type count) const + constexpr basic_string_span last(index_type count) const { return {span_.last(count)}; } template - constexpr basic_string_span subspan() const + constexpr basic_string_span subspan() const { return {span_.template subspan()}; } constexpr basic_string_span - subspan(index_type offset, index_type count = dynamic_extent) const + subspan(index_type offset, index_type count = dynamic_extent) const { return {span_.subspan(offset, count)}; } @@ -406,10 +411,10 @@ public: 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 { return span_.cend(); } - + constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); } constexpr reverse_iterator rend() const noexcept { return span_.rend(); } @@ -417,13 +422,13 @@ public: constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); } private: - static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) + static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) { return {sz, details::length_func()(sz, max)}; } template - static impl_type remove_z(element_type (&sz)[N]) + static impl_type remove_z(element_type (&sz)[N]) { return remove_z(&sz[0], narrow_cast(N)); } @@ -556,8 +561,9 @@ using cwzstring_span = basic_zstring_span; // operator == template ::value || std::is_convertible< - T, gsl::basic_string_span>>::value>> + class = std::enable_if_t< + details::is_basic_string_span::value || + std::is_convertible>>::value>> bool operator==(const gsl::basic_string_span& one, const T& other) noexcept { gsl::basic_string_span> tmp(other); @@ -568,11 +574,10 @@ bool operator==(const gsl::basic_string_span& one, const T& other #endif } -template < - class CharT, std::ptrdiff_t Extent, class T, - class = std::enable_if_t::value && - std::is_convertible>>::value - >> +template ::value && + std::is_convertible>>::value>> bool operator==(const T& one, const gsl::basic_string_span& other) noexcept { gsl::basic_string_span> tmp(one); -- cgit v1.2.3 From a88cfb168a32c2bccccd4ad698c6787e77ad6a4d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 27 Jul 2016 13:48:29 -0700 Subject: Fixed type-name mixup in string_span. --- include/string_span.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/string_span.h b/include/string_span.h index 1df7e6e..0ca3c51 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -362,21 +362,19 @@ public: { } - // first Count elements template constexpr basic_string_span first() const { return {span_.template first()}; } - constexpr basic_string_span first(index_type count) const + constexpr basic_string_span first(index_type count) const { return {span_.first(count)}; } - // last Count elements template - constexpr basic_string_span last() const + constexpr basic_string_span last() const { return {span_.template last()}; } -- cgit v1.2.3 From a0cf1ecc499b9a5609dd3436c5d83d08a3377f9d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 28 Jul 2016 17:27:22 -0700 Subject: Renamed as_span() to as_multi_span() for clarity. --- include/multi_span.h | 44 +++++++++++++-------------- tests/multi_span_tests.cpp | 72 ++++++++++++++++++++++---------------------- tests/strided_span_tests.cpp | 40 ++++++++++++------------ 3 files changed, 78 insertions(+), 78 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index cfe2d5c..a59c4dc 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1127,25 +1127,25 @@ namespace details }; template - T static_as_span_helper(Sep, Args... args) + T static_as_multi_span_helper(Sep, Args... args) { return T{narrow_cast(args)...}; } template std::enable_if_t< !std::is_same>::value && !std::is_same::value, T> - static_as_span_helper(Arg, Args... args) + static_as_multi_span_helper(Arg, Args... args) { - return static_as_span_helper(args...); + return static_as_multi_span_helper(args...); } template - T static_as_span_helper(dim val, Args... args) + T static_as_multi_span_helper(dim val, Args... args) { - return static_as_span_helper(args..., val.dvalue); + return static_as_multi_span_helper(args..., val.dvalue); } template - struct static_as_span_static_bounds_helper + struct static_as_multi_span_static_bounds_helper { using type = static_bounds<(Dimensions::value)...>; }; @@ -1599,13 +1599,13 @@ public: template 0), typename = std::enable_if_t> constexpr multi_span -as_span(SpanType s, Dimensions2... dims) +as_multi_span(SpanType s, Dimensions2... dims) { static_assert(details::is_multi_span::value, - "Variadic as_span() is for reshaping existing spans."); + "Variadic as_multi_span() is for reshaping existing spans."); using BoundsType = typename multi_span::bounds_type; - auto tobounds = details::static_as_span_helper(dims..., details::Sep{}); + auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); details::verifyBoundsReshape(s.bounds(), tobounds); return {s.data(), tobounds}; } @@ -1636,7 +1636,7 @@ multi_span as_writeable_bytes(multi_span s) noexcept // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(multi_span s) noexcept -> multi_span< +constexpr auto as_multi_span(multi_span s) noexcept -> multi_span< const U, static_cast( multi_span::bounds_type::static_size != dynamic_range ? (static_cast( @@ -1661,7 +1661,7 @@ constexpr auto as_span(multi_span s) noexcept -> mult // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_span(multi_span s) noexcept +constexpr auto as_multi_span(multi_span s) noexcept -> multi_span( multi_span::bounds_type::static_size != dynamic_range ? static_cast( @@ -1682,49 +1682,49 @@ constexpr auto as_span(multi_span s) noexcept } template -constexpr auto as_span(T* const& ptr, dim... args) +constexpr auto as_multi_span(T* const& ptr, dim... args) -> multi_span, Dimensions...> { return {reinterpret_cast*>(ptr), - details::static_as_span_helper>(args..., details::Sep{})}; + details::static_as_multi_span_helper>(args..., details::Sep{})}; } template -constexpr auto as_span(T* arr, std::ptrdiff_t len) -> +constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> typename details::SpanArrayTraits::type { return {reinterpret_cast*>(arr), len}; } template -constexpr auto as_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type { return {arr}; } template -constexpr multi_span as_span(const std::array& arr) +constexpr multi_span as_multi_span(const std::array& arr) { return {arr}; } template -constexpr multi_span as_span(const std::array&&) = delete; +constexpr multi_span as_multi_span(const std::array&&) = delete; template -constexpr multi_span as_span(std::array& arr) +constexpr multi_span as_multi_span(std::array& arr) { return {arr}; } template -constexpr multi_span as_span(T* begin, T* end) +constexpr multi_span as_multi_span(T* begin, T* end) { return {begin, end}; } template -constexpr auto as_span(Cont& arr) -> std::enable_if_t< +constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< !details::is_multi_span>::value, multi_span, dynamic_range>> { @@ -1733,13 +1733,13 @@ constexpr auto as_span(Cont& arr) -> std::enable_if_t< } template -constexpr auto as_span(Cont&& arr) -> std::enable_if_t< +constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< !details::is_multi_span>::value, multi_span, dynamic_range>> = delete; // from basic_string which doesn't have nonconst .data() member like other contiguous containers template -constexpr auto as_span(std::basic_string& str) +constexpr auto as_multi_span(std::basic_string& str) -> multi_span { Expects(str.size() < PTRDIFF_MAX); diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index 207428d..7432057 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -680,22 +680,22 @@ SUITE(multi_span_tests) { static_assert(Bounds::static_size == 60, "static bounds is wrong size"); } - TEST(as_span_reshape) + TEST(as_multi_span_reshape) { int a[3][4][5]; - auto av = as_span(a); + auto av = as_multi_span(a); fn(av.bounds()); - auto av2 = as_span(av, dim<60>()); - auto av3 = as_span(av2, dim<3>(), dim<4>(), dim<5>()); - auto av4 = as_span(av3, dim<4>(), dim<>(3), dim<5>()); - auto av5 = as_span(av4, dim<3>(), dim<4>(), dim<5>()); - auto av6 = as_span(av5, dim<12>(), dim<>(5)); + auto av2 = as_multi_span(av, dim<60>()); + auto av3 = as_multi_span(av2, dim<3>(), dim<4>(), dim<5>()); + auto av4 = as_multi_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av5 = as_multi_span(av4, dim<3>(), dim<4>(), dim<5>()); + auto av6 = as_multi_span(av5, dim<12>(), dim<>(5)); fill(av6.begin(), av6.end(), 1); auto av7 = as_bytes(av6); - auto av8 = as_span(av7); + auto av8 = as_multi_span(av7); CHECK(av8.size() == av6.size()); for (auto i = 0; i < av8.size(); i++) { @@ -949,12 +949,12 @@ SUITE(multi_span_tests) { { int arr[10][2]; - auto s1 = as_span(arr); + auto s1 = as_multi_span(arr); multi_span s2 = s1; CHECK(s1 == s2); - multi_span s3 = as_span(s1, dim<>(20)); + multi_span s3 = as_multi_span(s1, dim<>(20)); CHECK(s3 == s2 && s3 == s1); } @@ -1059,7 +1059,7 @@ SUITE(multi_span_tests) TEST(basics) { - auto ptr = as_span(new int[10], 10); + auto ptr = as_multi_span(new int[10], 10); fill(ptr.begin(), ptr.end(), 99); for (int num : ptr) { CHECK(num == 99); @@ -1071,7 +1071,7 @@ SUITE(multi_span_tests) TEST(bounds_checks) { int arr[10][2]; - auto av = as_span(arr); + auto av = as_multi_span(arr); fill(begin(av), end(av), 0); @@ -1111,7 +1111,7 @@ SUITE(multi_span_tests) { auto data = new int[4][3][5]; - auto av = as_span(data, 4); + auto av = as_multi_span(data, 4); CHECK(av.size() == 60); @@ -1122,7 +1122,7 @@ SUITE(multi_span_tests) CHECK(count == 34 * 60); overloaded_func(av, 34); - overloaded_func(as_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + overloaded_func(as_multi_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); // fixed_func(av, 34); delete[] data; @@ -1137,7 +1137,7 @@ SUITE(multi_span_tests) // size check will be done auto image_view = - as_span(as_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + as_multi_span(as_multi_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); iota(image_view.begin(), image_view.end(), 1); @@ -1160,12 +1160,12 @@ SUITE(multi_span_tests) } } - TEST(as_span) + TEST(as_multi_span) { { int* arr = new int[150]; - auto av = as_span(arr, dim<10>(), dim<>(3), dim<5>()); + auto av = as_multi_span(arr, dim<10>(), dim<>(3), dim<5>()); fill(av.begin(), av.end(), 24); overloaded_func(av, 24); @@ -1173,44 +1173,44 @@ SUITE(multi_span_tests) delete[] arr; array stdarr{0}; - auto av2 = as_span(stdarr); - overloaded_func(as_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + auto av2 = as_multi_span(stdarr); + overloaded_func(as_multi_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); string str = "ttttttttttttttt"; // size = 15 auto t = str.data(); (void) t; - auto av3 = as_span(str); - overloaded_func(as_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + auto av3 = as_multi_span(str); + overloaded_func(as_multi_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); } { string str; - multi_span strspan = as_span(str); + multi_span strspan = as_multi_span(str); (void) strspan; const string cstr; - multi_span cstrspan = as_span(cstr); + multi_span cstrspan = as_multi_span(cstr); (void) cstrspan; } { int a[3][4][5]; - auto av = as_span(a); + auto av = as_multi_span(a); const int(*b)[4][5]; b = a; - auto bv = as_span(b, 3); + auto bv = as_multi_span(b, 3); CHECK(av == bv); const std::array arr = {0.0, 0.0, 0.0}; - auto cv = as_span(arr); + auto cv = as_multi_span(arr); (void) cv; vector vec(3); - auto dv = as_span(vec); + auto dv = as_multi_span(vec); (void) dv; #ifdef CONFIRM_COMPILATION_ERRORS - auto dv2 = as_span(std::move(vec)); + auto dv2 = as_multi_span(std::move(vec)); #endif } } @@ -1258,7 +1258,7 @@ SUITE(multi_span_tests) CHECK(av[i] == 4); - auto av2 = as_span(av, dim<4>(), dim<>(2)); + auto av2 = as_multi_span(av, dim<4>(), dim<>(2)); ptrdiff_t a2[2] = {0, 1}; index<2> i2 = a2; @@ -1486,21 +1486,21 @@ SUITE(multi_span_tests) arr[i] = i; } - auto av = as_span(arr, size); + auto av = as_multi_span(arr, size); // first bound is dynamic { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } // second bound is dynamic { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } // both bounds are dynamic { - multi_span av2 = as_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); iterate_second_column(av2); } @@ -1521,7 +1521,7 @@ SUITE(multi_span_tests) CHECK_THROW(av1[10][3][4], fail_fast); - multi_span av2 = as_span(av1, dim<>(5), dim<6>(), dim<4>()); + multi_span av2 = as_multi_span(av1, dim<>(5), dim<6>(), dim<4>()); (void) av2; } @@ -1562,13 +1562,13 @@ SUITE(multi_span_tests) #ifdef CONFIRM_COMPILATION_ERRORS { multi_span av = arr; - multi_span av2 = av.as_span(dim<2>(), dim<2>()); + multi_span av2 = av.as_multi_span(dim<2>(), dim<2>()); } #endif { multi_span av = arr; - multi_span av2 = as_span(av, dim<>(2), dim<>(2)); + multi_span av2 = as_multi_span(av, dim<>(2), dim<>(2)); auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; CHECK(workaround_macro()); } diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index 9fcdfeb..19056b1 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -39,7 +39,7 @@ SUITE(strided_span_tests) { int a[30][4][5]; - auto av = as_span(a); + auto av = as_multi_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; @@ -49,7 +49,7 @@ SUITE(strided_span_tests) { std::vector data(5 * 10); std::iota(begin(data), end(data), 0); - const multi_span av = as_span(multi_span{data}, dim<5>(), dim<10>()); + const multi_span av = as_multi_span(multi_span{data}, dim<5>(), dim<10>()); strided_span av_section_1 = av.section({ 1, 2 }, { 3, 4 }); CHECK((av_section_1[{0, 0}] == 12)); @@ -258,7 +258,7 @@ SUITE(strided_span_tests) { std::vector data(5 * 10); std::iota(begin(data), end(data), 0); - const multi_span src = as_span(multi_span{data}, dim<5>(), dim<10>()); + const multi_span src = as_multi_span(multi_span{data}, dim<5>(), dim<10>()); const strided_span sav{ src, {{5, 10}, {10, 1}} }; #ifdef CONFIRM_COMPILATION_ERRORS @@ -413,18 +413,18 @@ SUITE(strided_span_tests) strided_span sav2{ arr, { 1,1,1 } }; strided_span sav3{ av, { 1 } }; strided_span sav4{ av, { 1,1,1 } }; - strided_span sav5{ av.as_span(dim<2>(), dim<2>()), { 1 } }; - strided_span sav6{ av.as_span(dim<2>(), dim<2>()), { 1,1,1 } }; - strided_span sav7{ av.as_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; + strided_span sav5{ av.as_multi_span(dim<2>(), dim<2>()), { 1 } }; + strided_span sav6{ av.as_multi_span(dim<2>(), dim<2>()), { 1,1,1 } }; + strided_span sav7{ av.as_multi_span(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; index<1> index{ 0, 1 }; strided_span sav8{ arr,{ 1,{ 1,1 } } }; strided_span sav9{ arr,{ { 1,1 },{ 1,1 } } }; strided_span sav10{ av,{ 1,{ 1,1 } } }; strided_span sav11{ av,{ { 1,1 },{ 1,1 } } }; - strided_span sav12{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; - strided_span sav13{ av.as_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; - strided_span sav14{ av.as_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; + strided_span sav12{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_span sav13{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_span sav14{ av.as_multi_span(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; } #endif } @@ -463,7 +463,7 @@ SUITE(strided_span_tests) // retype strided array with regular strides - from multi_span { strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; strided_span sav3 = sav2.as_strided_span(); CHECK(sav3[0][0] == 0); @@ -475,7 +475,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - last dimension of the array is too small { strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } }; - multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -483,7 +483,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides are too small { strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; - multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -491,7 +491,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - last dimension does not divide by the new typesize { strided_bounds<2> bounds{ { 2,6 },{ 4, 1 } }; - multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -499,7 +499,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides does not divide by the new typesize { strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } }; - multi_span bytes2 = as_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -606,7 +606,7 @@ SUITE(strided_span_tests) arr[2 * i + 1] = i; } - auto av = as_span(arr, 8); + auto av = as_multi_span(arr, 8); iterate_every_other_element(av); delete[] arr; @@ -670,22 +670,22 @@ SUITE(strided_span_tests) } { - auto av = as_span(as_span(arr, 24), dim<3>(), dim<4>(), dim<2>()); + auto av = as_multi_span(as_multi_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>()); + auto av = as_multi_span(as_multi_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>()); + auto av = as_multi_span(as_multi_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)); + auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<>(2)); iterate_second_slice(av); } delete[] arr; @@ -704,7 +704,7 @@ SUITE(strided_span_tests) 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)); + auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim<>(d1), dim<>(d2)); CHECK(av.bounds().index_bounds()[0] == 4); CHECK(av.bounds().index_bounds()[1] == 12); -- cgit v1.2.3 From 8e31f53f8a3132c497363bd87f03204d3d7969af Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Fri, 29 Jul 2016 11:16:06 -0700 Subject: Building clean with MSVC12. --- include/multi_span.h | 7 ++++--- include/span.h | 22 ++++++++++++++++++++-- include/string_span.h | 23 +---------------------- tests/span_tests.cpp | 8 ++++++++ tests/utils_tests.cpp | 2 +- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index a59c4dc..e35fc33 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1594,12 +1594,13 @@ public: // Free functions for manipulating spans // + // reshape a multi_span into a different dimensionality // DimCount and Enabled here are workarounds for a bug in MSVC 2015 template 0), typename = std::enable_if_t> -constexpr multi_span -as_multi_span(SpanType s, Dimensions2... dims) + bool Enabled = (DimCount > 0), typename = std::enable_if_t > +constexpr auto +as_multi_span(SpanType s, Dimensions2... dims) -> multi_span { static_assert(details::is_multi_span::value, "Variadic as_multi_span() is for reshaping existing spans."); diff --git a/include/span.h b/include/span.h index 4d8e877..44e1b8d 100644 --- a/include/span.h +++ b/include/span.h @@ -49,7 +49,8 @@ #if _MSC_VER <= 1800 #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR +#define GSL_MSVC_NO_CPP14_STD_EQUAL // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION @@ -57,6 +58,9 @@ #define noexcept /* nothing */ #endif +#pragma push_macro("alignof") +#define alignof __alignof + // turn off some misguided warnings #pragma warning(push) #pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior @@ -541,7 +545,11 @@ public: } constexpr span(const span& other) noexcept = default; +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr span(span&& other) noexcept = default; +#else + constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {} +#endif template < class OtherElementType, std::ptrdiff_t OtherExtent, @@ -567,8 +575,12 @@ public: ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; - constexpr span& operator=(span&& other) noexcept = default; +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr span& operator=(span&& other) noexcept = default; +#else + constexpr span& operator=(span&& other) noexcept { storage_ = std::move(other.storage_); return *this; } +#endif // [span.sub], span subviews template constexpr span first() const @@ -669,7 +681,11 @@ template & l, const span& r) { +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin()); +#else return std::equal(l.begin(), l.end(), r.begin(), r.end()); +#endif } template @@ -756,6 +772,8 @@ as_writeable_bytes(span s) noexcept #pragma pop_macro("noexcept") #endif // GSL_THROW_ON_CONTRACT_VIOLATION +#pragma pop_macro("alignof") + #undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG #endif // _MSC_VER <= 1800 diff --git a/include/string_span.h b/include/string_span.h index 0ca3c51..3fe826f 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -24,6 +24,7 @@ #include "span.h" #include #include +#include #ifdef _MSC_VER @@ -313,7 +314,6 @@ public: template ::value && - !details::is_span::value && std::is_convertible::value && std::is_convertible().data())>::value>> @@ -324,7 +324,6 @@ public: template ::value && - !details::is_span::value && std::is_convertible::value && std::is_convertible().data())>::value>> @@ -332,26 +331,6 @@ public: { } -#ifndef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE - // from span - template , impl_type>::value>> - constexpr basic_string_span(const span& other) : span_(other) - { - } -#else - // from span - constexpr basic_string_span(span other) : span_(other) {} - - template , value_type>::value>> - constexpr basic_string_span(const span, Extent>& other) - : span_(other) - { - } -#endif - // from string_span template < class OtherValueType, std::ptrdiff_t OtherExtent, diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 77b799a..8c9829d 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -839,7 +839,9 @@ SUITE(span_tests) CHECK(it - beyond == 0); for (auto& n : s) + { CHECK(n == 5); + } } } @@ -881,7 +883,9 @@ SUITE(span_tests) CHECK(it - beyond == 0); for (auto& n : s) + { CHECK(n == 5); + } } } @@ -923,7 +927,9 @@ SUITE(span_tests) CHECK(it - beyond == 0); for (auto& n : s) + { CHECK(n == 5); + } } } @@ -965,7 +971,9 @@ SUITE(span_tests) CHECK(it - beyond == 0); for (auto& n : s) + { CHECK(n == 5); + } } } diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index a46d6e4..11582de 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -103,7 +103,7 @@ SUITE(utils_tests) CHECK(narrow(int32_t(0)) == 0); CHECK(narrow(int32_t(1)) == 1); - CHECK(narrow(int32_max) == int32_max); + CHECK(narrow(int32_max) == static_cast(int32_max)); CHECK_THROW(narrow(int32_t(-1)), narrowing_error); CHECK_THROW(narrow(int32_min), narrowing_error); -- cgit v1.2.3 From 1bd2d04c2e76f0eed41507d830070e2499b1290d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 1 Aug 2016 13:10:02 -0700 Subject: Another round of clang-formatting. --- include/multi_span.h | 10 +++++----- include/span.h | 6 +++++- include/string_span.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index e35fc33..a8a1af2 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1594,13 +1594,12 @@ public: // Free functions for manipulating spans // - // reshape a multi_span into a different dimensionality // DimCount and Enabled here are workarounds for a bug in MSVC 2015 template 0), typename = std::enable_if_t > -constexpr auto -as_multi_span(SpanType s, Dimensions2... dims) -> multi_span + bool Enabled = (DimCount > 0), typename = std::enable_if_t> +constexpr auto as_multi_span(SpanType s, Dimensions2... dims) + -> multi_span { static_assert(details::is_multi_span::value, "Variadic as_multi_span() is for reshaping existing spans."); @@ -1687,7 +1686,8 @@ constexpr auto as_multi_span(T* const& ptr, dim... args) -> multi_span, Dimensions...> { return {reinterpret_cast*>(ptr), - details::static_as_multi_span_helper>(args..., details::Sep{})}; + details::static_as_multi_span_helper>(args..., + details::Sep{})}; } template diff --git a/include/span.h b/include/span.h index 44e1b8d..98e08fd 100644 --- a/include/span.h +++ b/include/span.h @@ -579,7 +579,11 @@ public: #ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR constexpr span& operator=(span&& other) noexcept = default; #else - constexpr span& operator=(span&& other) noexcept { storage_ = std::move(other.storage_); return *this; } + constexpr span& operator=(span&& other) noexcept + { + storage_ = std::move(other.storage_); + return *this; + } #endif // [span.sub], span subviews template diff --git a/include/string_span.h b/include/string_span.h index 3fe826f..e18e07a 100644 --- a/include/string_span.h +++ b/include/string_span.h @@ -22,9 +22,9 @@ #include "gsl_assert.h" #include "gsl_util.h" #include "span.h" +#include #include #include -#include #ifdef _MSC_VER -- cgit v1.2.3 From 94afa1fbd7e11dabe2e2eff100c7588e37ae3cf6 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 1 Aug 2016 18:49:48 -0700 Subject: Removed unnecessary inheritance between iterators. --- include/span.h | 78 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/include/span.h b/include/span.h index 98e08fd..004afc7 100644 --- a/include/span.h +++ b/include/span.h @@ -1,3 +1,4 @@ + /////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2015 Microsoft Corporation. All rights reserved. @@ -191,6 +192,7 @@ namespace details Expects(span_); return (*span_)[index_]; } + constexpr pointer operator->() const { Expects(span_); @@ -300,10 +302,8 @@ namespace details }; template - class span_iterator : public const_span_iterator + class span_iterator { - using base_type = const_span_iterator; - public: using iterator_category = std::random_access_iterator_tag; using value_type = typename Span::element_type; @@ -312,67 +312,88 @@ namespace details using pointer = value_type*; using reference = value_type&; - constexpr span_iterator() : base_type() {} + constexpr span_iterator() : span_iterator(nullptr, 0) {} constexpr span_iterator(const Span* span, typename Span::index_type index) - : base_type(span, index) + : span_(span), index_(index) { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); } constexpr reference operator*() const { - return const_cast(base_type::operator*()); + Expects(span_); + return (*span_)[index_]; } + constexpr pointer operator->() const { - return const_cast(base_type::operator->()); + Expects(span_); + return &((*span_)[index_]); } constexpr span_iterator& operator++() noexcept { - base_type::operator++(); + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; return *this; } - constexpr span_iterator operator++(int) noexcept { return base_type::operator++(1); } + constexpr span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } constexpr span_iterator& operator--() noexcept { - base_type::operator--(); + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; return *this; } - constexpr span_iterator operator--(int) noexcept { return base_type::operator--(1); } + constexpr span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } constexpr span_iterator operator+(difference_type n) const noexcept { - return {base_type::operator+(n)}; + auto ret = *this; + return ret += n; } constexpr span_iterator& operator+=(difference_type n) noexcept { - return {base_type::operator+=(n)}; + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; } constexpr span_iterator operator-(difference_type n) const noexcept { - return base_type::operator-(n); + auto ret = *this; + return ret -= n; } constexpr span_iterator& operator-=(difference_type n) noexcept { - return base_type::operator-=(n); + return *this += -n; } constexpr difference_type operator-(const span_iterator& rhs) const noexcept { - return base_type::operator-(rhs); + Expects(span_ == rhs.span_); + return index_ - rhs.index_; } constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } constexpr bool operator==(const span_iterator& rhs) const noexcept { - return base_type::operator==(rhs); + return span_ == rhs.span_ && index_ == rhs.index_; } constexpr bool operator!=(const span_iterator& rhs) const noexcept @@ -382,7 +403,8 @@ namespace details constexpr bool operator<(const span_iterator& rhs) const noexcept { - return base_type::operator<(rhs); + Expects(span_ == rhs.span_); + return index_ < rhs.index_; } constexpr bool operator<=(const span_iterator& rhs) const noexcept @@ -397,9 +419,15 @@ namespace details return !(rhs > *this); } - void swap(span_iterator& rhs) noexcept { base_type::swap(rhs); } + void swap(span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(span_, rhs.span_); + } + private: - constexpr span_iterator(const base_type& base) : base_type(base) {} + const Span* span_; + std::ptrdiff_t index_; }; template @@ -487,7 +515,7 @@ public: using reference = element_type&; using iterator = details::span_iterator>; - using const_iterator = details::const_span_iterator; + using const_iterator = details::const_span_iterator>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -651,11 +679,11 @@ public: const_iterator cbegin() const noexcept { return {this, 0}; } const_iterator cend() const noexcept { return {this, length()}; } - reverse_iterator rbegin() const noexcept { return reverse_iterator{{this, length()}}; } - reverse_iterator rend() const noexcept { return reverse_iterator{{this, 0}}; } + reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - const_reverse_iterator crbegin() const noexcept { return reverse_iterator{{this, length()}}; } - const_reverse_iterator crend() const noexcept { return reverse_iterator{{this, 0}}; } + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } private: // this implementation detail class lets us take advantage of the -- cgit v1.2.3 From 6c7be2c8ee2052f17bcfb43d9e97b28f32840283 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 1 Aug 2016 21:41:20 -0700 Subject: clang-format run. --- include/span.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/span.h b/include/span.h index 004afc7..a5d2823 100644 --- a/include/span.h +++ b/include/span.h @@ -302,7 +302,7 @@ namespace details }; template - class span_iterator + class span_iterator { public: using iterator_category = std::random_access_iterator_tag; @@ -378,10 +378,7 @@ namespace details return ret -= n; } - constexpr span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } constexpr difference_type operator-(const span_iterator& rhs) const noexcept { @@ -515,7 +512,7 @@ public: using reference = element_type&; using iterator = details::span_iterator>; - using const_iterator = details::const_span_iterator>; + using const_iterator = details::const_span_iterator>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; -- cgit v1.2.3 From 3836e124d8a80dfdaa009591bf34ee783de418e8 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 2 Aug 2016 14:59:57 -0700 Subject: Added code of conduct statement to README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 35e93e4..3c440c2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the e > NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. +# Project Code of Conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + # Quick Start ## Supported Platforms The test suite that exercises GSL has been built and passes successfully on the following platforms: -- cgit v1.2.3 From ebce4920d66816f8c9addb288cfe67bef41f07a2 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 2 Aug 2016 16:22:18 -0700 Subject: Fixed #308 - a mismatched pragma. --- include/span.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/span.h b/include/span.h index a612983..b7a3d77 100644 --- a/include/span.h +++ b/include/span.h @@ -2221,10 +2221,13 @@ general_span_iterator operator+(typename general_span_iterator::diff #undef noexcept #ifdef _MSC_VER -#pragma warning(pop) #pragma pop_macro("noexcept") #endif #endif // GSL_THROW_ON_CONTRACT_VIOLATION +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif // GSL_SPAN_H -- cgit v1.2.3 From c4817358aa9c5d2b9b64fa959df4bab0369d2470 Mon Sep 17 00:00:00 2001 From: Som1Lse Date: Wed, 3 Aug 2016 22:28:25 +0200 Subject: Implemented https://github.com/Microsoft/GSL/issues/260 --- include/multi_span.h | 22 ++++++++++++++++------ tests/multi_span_tests.cpp | 28 ++++++++++++++-------------- tests/strided_span_tests.cpp | 18 +++++++++--------- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index a8a1af2..c883fb0 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1053,18 +1053,28 @@ template class general_span_iterator; template -struct dim +struct dim_t { static const std::ptrdiff_t value = DimSize; }; template <> -struct dim +struct dim_t { static const std::ptrdiff_t value = dynamic_range; const std::ptrdiff_t dvalue; - dim(std::ptrdiff_t size) : dvalue(size) {} + dim_t(std::ptrdiff_t size) : dvalue(size) {} }; +template +constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { + return dim_t(); +} + +template +constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { + return dim_t<>(n); +} + template class multi_span; @@ -1133,13 +1143,13 @@ namespace details } template std::enable_if_t< - !std::is_same>::value && !std::is_same::value, T> + !std::is_same>::value && !std::is_same::value, T> static_as_multi_span_helper(Arg, Args... args) { return static_as_multi_span_helper(args...); } template - T static_as_multi_span_helper(dim val, Args... args) + T static_as_multi_span_helper(dim_t val, Args... args) { return static_as_multi_span_helper(args..., val.dvalue); } @@ -1682,7 +1692,7 @@ constexpr auto as_multi_span(multi_span s) noexcept } template -constexpr auto as_multi_span(T* const& ptr, dim... args) +constexpr auto as_multi_span(T* const& ptr, dim_t... args) -> multi_span, Dimensions...> { return {reinterpret_cast*>(ptr), diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index 7432057..003d236 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -687,9 +687,9 @@ SUITE(multi_span_tests) fn(av.bounds()); auto av2 = as_multi_span(av, dim<60>()); auto av3 = as_multi_span(av2, dim<3>(), dim<4>(), dim<5>()); - auto av4 = as_multi_span(av3, dim<4>(), dim<>(3), dim<5>()); + auto av4 = as_multi_span(av3, dim<4>(), dim(3), dim<5>()); auto av5 = as_multi_span(av4, dim<3>(), dim<4>(), dim<5>()); - auto av6 = as_multi_span(av5, dim<12>(), dim<>(5)); + auto av6 = as_multi_span(av5, dim<12>(), dim(5)); fill(av6.begin(), av6.end(), 1); @@ -954,7 +954,7 @@ SUITE(multi_span_tests) CHECK(s1 == s2); - multi_span s3 = as_multi_span(s1, dim<>(20)); + multi_span s3 = as_multi_span(s1, dim(20)); CHECK(s3 == s2 && s3 == s1); } @@ -1122,7 +1122,7 @@ SUITE(multi_span_tests) CHECK(count == 34 * 60); overloaded_func(av, 34); - overloaded_func(as_multi_span(av, dim<>(4), dim<>(3), dim<>(5)), 34); + overloaded_func(as_multi_span(av, dim(4), dim(3), dim(5)), 34); // fixed_func(av, 34); delete[] data; @@ -1137,7 +1137,7 @@ SUITE(multi_span_tests) // size check will be done auto image_view = - as_multi_span(as_multi_span(image_ptr, imgSize), dim<>(height), dim<>(width), dim<3>()); + as_multi_span(as_multi_span(image_ptr, imgSize), dim(height), dim(width), dim<3>()); iota(image_view.begin(), image_view.end(), 1); @@ -1165,7 +1165,7 @@ SUITE(multi_span_tests) { int* arr = new int[150]; - auto av = as_multi_span(arr, dim<10>(), dim<>(3), dim<5>()); + auto av = as_multi_span(arr, dim<10>(), dim(3), dim<5>()); fill(av.begin(), av.end(), 24); overloaded_func(av, 24); @@ -1174,13 +1174,13 @@ SUITE(multi_span_tests) array stdarr{0}; auto av2 = as_multi_span(stdarr); - overloaded_func(as_multi_span(av2, dim<>(1), dim<3>(), dim<5>()), 0); + overloaded_func(as_multi_span(av2, dim(1), dim<3>(), dim<5>()), 0); string str = "ttttttttttttttt"; // size = 15 auto t = str.data(); (void) t; auto av3 = as_multi_span(str); - overloaded_func(as_multi_span(av3, dim<>(1), dim<3>(), dim<5>()), 't'); + overloaded_func(as_multi_span(av3, dim(1), dim<3>(), dim<5>()), 't'); } { @@ -1258,7 +1258,7 @@ SUITE(multi_span_tests) CHECK(av[i] == 4); - auto av2 = as_multi_span(av, dim<4>(), dim<>(2)); + auto av2 = as_multi_span(av, dim<4>(), dim(2)); ptrdiff_t a2[2] = {0, 1}; index<2> i2 = a2; @@ -1490,17 +1490,17 @@ SUITE(multi_span_tests) // first bound is dynamic { - multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim(height), dim(width)); iterate_second_column(av2); } // second bound is dynamic { - multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim(height), dim(width)); iterate_second_column(av2); } // both bounds are dynamic { - multi_span av2 = as_multi_span(av, dim<>(height), dim<>(width)); + multi_span av2 = as_multi_span(av, dim(height), dim(width)); iterate_second_column(av2); } @@ -1521,7 +1521,7 @@ SUITE(multi_span_tests) CHECK_THROW(av1[10][3][4], fail_fast); - multi_span av2 = as_multi_span(av1, dim<>(5), dim<6>(), dim<4>()); + multi_span av2 = as_multi_span(av1, dim(5), dim<6>(), dim<4>()); (void) av2; } @@ -1568,7 +1568,7 @@ SUITE(multi_span_tests) { multi_span av = arr; - multi_span av2 = as_multi_span(av, dim<>(2), dim<>(2)); + multi_span av2 = as_multi_span(av, dim(2), dim(2)); auto workaround_macro = [&]() { return av2[{1, 0}] == 2; }; CHECK(workaround_macro()); } diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index 19056b1..b81a5e7 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -463,7 +463,7 @@ SUITE(strided_span_tests) // retype strided array with regular strides - from multi_span { strided_bounds<2> bounds{ { 2, bytes.size() / 4 }, { bytes.size() / 2, 1 } }; - multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; strided_span sav3 = sav2.as_strided_span(); CHECK(sav3[0][0] == 0); @@ -475,7 +475,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - last dimension of the array is too small { strided_bounds<2> bounds{ { 4,2 },{ 4, 1 } }; - multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -483,7 +483,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides are too small { strided_bounds<2> bounds{ { 4,2 },{ 2, 1 } }; - multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -491,7 +491,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - last dimension does not divide by the new typesize { strided_bounds<2> bounds{ { 2,6 },{ 4, 1 } }; - multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -499,7 +499,7 @@ SUITE(strided_span_tests) // retype strided array with not enough elements - strides does not divide by the new typesize { strided_bounds<2> bounds{ { 2, 1 },{ 6, 1 } }; - multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim<>(bytes.size() / 2)); + multi_span bytes2 = as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2)); strided_span sav2{ bytes2, bounds }; CHECK_THROW(sav2.as_strided_span(), fail_fast); } @@ -675,17 +675,17 @@ SUITE(strided_span_tests) } { - auto av = as_multi_span(as_multi_span(arr, 24), dim<>(3), dim<4>(), dim<2>()); + auto av = as_multi_span(as_multi_span(arr, 24), dim(3), dim<4>(), dim<2>()); iterate_second_slice(av); } { - auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<>(4), dim<2>()); + auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim(4), dim<2>()); iterate_second_slice(av); } { - auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<>(2)); + auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim(2)); iterate_second_slice(av); } delete[] arr; @@ -704,7 +704,7 @@ SUITE(strided_span_tests) auto d1 = sizeof(int) * 12 / d2; // convert to 4x12 array of bytes - auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim<>(d1), dim<>(d2)); + auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim(d1), dim(d2)); CHECK(av.bounds().index_bounds()[0] == 4); CHECK(av.bounds().index_bounds()[1] == 12); -- cgit v1.2.3 From 82389aa630a28ec520b115b9bcb770a5ae495860 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 8 Aug 2016 12:06:47 -0700 Subject: Fixed up iterator implementation to allow conversion from iterator to const_iterator. --- include/span.h | 220 ++++++++++----------------------------------------- tests/span_tests.cpp | 98 ++++++++++++++++++----- 2 files changed, 121 insertions(+), 197 deletions(-) diff --git a/include/span.h b/include/span.h index a5d2823..ca35d8c 100644 --- a/include/span.h +++ b/include/span.h @@ -166,159 +166,29 @@ namespace details { }; - template - class const_span_iterator - { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; - - using const_pointer = std::add_const_t; - using pointer = const_pointer; - - using const_reference = std::add_const_t; - using reference = const_reference; - - constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} - constexpr const_span_iterator(const Span* span, typename Span::index_type index) - : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } - - constexpr reference operator*() const - { - Expects(span_); - return (*span_)[index_]; - } - - constexpr pointer operator->() const - { - Expects(span_); - return &((*span_)[index_]); - } - - constexpr const_span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } - - constexpr const_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr const_span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } - - constexpr const_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr const_span_iterator operator+(difference_type n) const noexcept - { - auto ret = *this; - return ret += n; - } - - constexpr const_span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } - - constexpr const_span_iterator operator-(difference_type n) const noexcept - { - auto ret = *this; - return ret -= n; - } - - constexpr const_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const const_span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } - - constexpr bool operator!=(const const_span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } - - constexpr bool operator<=(const const_span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const const_span_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const const_span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(const_span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(span_, rhs.span_); - } - - private: - const Span* span_; - std::ptrdiff_t index_; - }; - - template + template class span_iterator { public: using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; + using value_type = std::conditional_t, typename Span::element_type>; + using difference_type = typename Span::index_type; - using pointer = value_type*; - using reference = value_type&; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; - constexpr span_iterator() : span_iterator(nullptr, 0) {} + constexpr span_iterator() : span_iterator(nullptr, 0) noexcept {} + constexpr span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) { Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); } + friend class span_iterator; + constexpr span_iterator(const span_iterator& other) noexcept + : span_iterator(other.span_, other.index_) {} + constexpr reference operator*() const { Expects(span_); @@ -378,7 +248,10 @@ namespace details return ret -= n; } - constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + constexpr span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } constexpr difference_type operator-(const span_iterator& rhs) const noexcept { @@ -388,32 +261,35 @@ namespace details constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - constexpr bool operator==(const span_iterator& rhs) const noexcept + constexpr friend bool operator==(const span_iterator& lhs, const span_iterator& rhs) noexcept { - return span_ == rhs.span_ && index_ == rhs.index_; + return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; } - constexpr bool operator!=(const span_iterator& rhs) const noexcept + constexpr friend bool operator!=(const span_iterator& lhs, const span_iterator& rhs) noexcept { - return !(*this == rhs); + return !(lhs == rhs); } - constexpr bool operator<(const span_iterator& rhs) const noexcept + constexpr friend bool operator<(const span_iterator& lhs, const span_iterator& rhs) noexcept { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; + Expects(lhs.span_ == rhs.span_); + return lhs.index_ < rhs.index_; } - constexpr bool operator<=(const span_iterator& rhs) const noexcept + constexpr friend bool operator<=(const span_iterator& lhs, const span_iterator& rhs) noexcept { - return !(rhs < *this); + return !(rhs < lhs); } - constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + constexpr friend bool operator>(const span_iterator& lhs, const span_iterator& rhs) noexcept + { + return rhs < lhs; + } - constexpr bool operator>=(const span_iterator& rhs) const noexcept + constexpr friend bool operator>=(const span_iterator& lhs, const span_iterator& rhs) noexcept { - return !(rhs > *this); + return !(rhs > lhs); } void swap(span_iterator& rhs) noexcept @@ -422,37 +298,23 @@ namespace details std::swap(span_, rhs.span_); } - private: + protected: const Span* span_; std::ptrdiff_t index_; }; - template - constexpr const_span_iterator - operator+(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs + n; - } - - template - constexpr const_span_iterator - operator-(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs - n; - } - - template - constexpr span_iterator operator+(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept + template + constexpr span_iterator + operator+(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept { return rhs + n; } - template - constexpr span_iterator operator-(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept + template + constexpr span_iterator + operator-(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept { return rhs - n; } @@ -511,8 +373,8 @@ public: using pointer = element_type*; using reference = element_type&; - using iterator = details::span_iterator>; - using const_iterator = details::const_span_iterator>; + using iterator = details::span_iterator, false>; + using const_iterator = details::span_iterator, true>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8c9829d..4058463 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -787,20 +787,90 @@ SUITE(span_tests) } } - TEST(iterator) + TEST(iterator_default_init) { span::iterator it1; span::iterator it2; CHECK(it1 == it2); } - TEST(const_iterator) + TEST(const_iterator_default_init) { span::const_iterator it1; span::const_iterator it2; CHECK(it1 == it2); } + TEST(iterator_conversions) + { + span::iterator badIt; + span::const_iterator badConstIt; + CHECK(badIt == badConstIt); + + int a[] = { 1, 2, 3, 4 }; + span s = a; + + auto it = s.begin(); + auto cit = s.cbegin(); + + CHECK(it == cit); + CHECK(cit == it); + + span::const_iterator cit2 = it; + CHECK(cit2 == cit); + + span::const_iterator cit3 = it + 4; + CHECK(cit3 == s.cend()); + } + + TEST(iterator_comparisons) + { + int a[] = { 1, 2, 3, 4 }; + { + span s = a; + span::iterator it = s.begin(); + auto it2 = it + 1; + span::const_iterator cit = s.cbegin(); + auto cit2 = s.cbegin(); + + CHECK(it == cit); + CHECK(cit == it); + CHECK(it == it); + CHECK(cit == cit); + CHECK(cit == s.begin()); + CHECK(s.begin() == cit); + CHECK(s.cbegin() == cit); + CHECK(it == s.begin()); + CHECK(s.begin() == it); + + CHECK(it != it2); + CHECK(it2 != it); + CHECK(it != s.end()); + CHECK(it2 != s.end()); + CHECK(s.end() != it); + CHECK(it2 != cit); + CHECK(cit != it2); + + CHECK(it < it2); + CHECK(it <= it2); + CHECK(it2 <= s.end()); + CHECK(it < s.end()); + CHECK(it <= cit); + CHECK(cit <= it); + CHECK(cit < it2); + CHECK(cit <= it2); + CHECK(cit < s.end()); + CHECK(cit <= s.end()); + + CHECK(it2 > it); + CHECK(it2 >= it); + CHECK(s.end() > it2); + CHECK(s.end() >= it2); + CHECK(it2 > cit); + CHECK(it2 >= cit); + } + } + TEST(begin_end) { { @@ -867,25 +937,21 @@ SUITE(span_tests) ++it; CHECK(it - first == 1); CHECK(*it == 2); - *it = 22; - CHECK(*it == 22); CHECK(beyond - it == 3); + int last = 0; it = first; CHECK(it == first); while (it != s.cend()) { - *it = 5; + CHECK(*it == last + 1); + + last = *it; ++it; } CHECK(it == beyond); CHECK(it - beyond == 0); - - for (auto& n : s) - { - CHECK(n == 5); - } } } @@ -955,25 +1021,21 @@ SUITE(span_tests) ++it; CHECK(it - first == 1); CHECK(*it == 3); - *it = 22; - CHECK(*it == 22); CHECK(beyond - it == 3); it = first; CHECK(it == first); + int last = 5; while (it != s.crend()) { - *it = 5; + CHECK(*it == last - 1); + last = *it; + ++it; } CHECK(it == beyond); CHECK(it - beyond == 0); - - for (auto& n : s) - { - CHECK(n == 5); - } } } -- cgit v1.2.3 From 0dd5f56bed0362c84819d84ed1a0d77b025ad422 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 8 Aug 2016 13:33:02 -0700 Subject: Fixed unused variable and ran clang-format. Tested on gcc/clang. --- include/multi_span.h | 6 ++++-- include/span.h | 27 ++++++++++++++++----------- tests/span_tests.cpp | 1 - 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/include/multi_span.h b/include/multi_span.h index c883fb0..b31909d 100644 --- a/include/multi_span.h +++ b/include/multi_span.h @@ -1066,12 +1066,14 @@ struct dim_t }; template -constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { +constexpr std::enable_if_t<(N >= 0), dim_t> dim() noexcept +{ return dim_t(); } template -constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { +constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept +{ return dim_t<>(n); } diff --git a/include/span.h b/include/span.h index ca35d8c..5b2e427 100644 --- a/include/span.h +++ b/include/span.h @@ -171,14 +171,16 @@ namespace details { public: using iterator_category = std::random_access_iterator_tag; - using value_type = std::conditional_t, typename Span::element_type>; + using value_type = + std::conditional_t, + typename Span::element_type>; using difference_type = typename Span::index_type; using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; constexpr span_iterator() : span_iterator(nullptr, 0) noexcept {} - + constexpr span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) { @@ -187,7 +189,9 @@ namespace details friend class span_iterator; constexpr span_iterator(const span_iterator& other) noexcept - : span_iterator(other.span_, other.index_) {} + : span_iterator(other.span_, other.index_) + { + } constexpr reference operator*() const { @@ -248,10 +252,7 @@ namespace details return ret -= n; } - constexpr span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } + constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } constexpr difference_type operator-(const span_iterator& rhs) const noexcept { @@ -261,12 +262,14 @@ namespace details constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - constexpr friend bool operator==(const span_iterator& lhs, const span_iterator& rhs) noexcept + constexpr friend bool operator==(const span_iterator& lhs, + const span_iterator& rhs) noexcept { return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; } - constexpr friend bool operator!=(const span_iterator& lhs, const span_iterator& rhs) noexcept + constexpr friend bool operator!=(const span_iterator& lhs, + const span_iterator& rhs) noexcept { return !(lhs == rhs); } @@ -277,7 +280,8 @@ namespace details return lhs.index_ < rhs.index_; } - constexpr friend bool operator<=(const span_iterator& lhs, const span_iterator& rhs) noexcept + constexpr friend bool operator<=(const span_iterator& lhs, + const span_iterator& rhs) noexcept { return !(rhs < lhs); } @@ -287,7 +291,8 @@ namespace details return rhs < lhs; } - constexpr friend bool operator>=(const span_iterator& lhs, const span_iterator& rhs) noexcept + constexpr friend bool operator>=(const span_iterator& lhs, + const span_iterator& rhs) noexcept { return !(rhs > lhs); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 4058463..55610c7 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -831,7 +831,6 @@ SUITE(span_tests) span::iterator it = s.begin(); auto it2 = it + 1; span::const_iterator cit = s.cbegin(); - auto cit2 = s.cbegin(); CHECK(it == cit); CHECK(cit == it); -- cgit v1.2.3 From 32ee66d3201179b261cf498ab41b11966aab6c35 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 8 Aug 2016 13:48:12 -0700 Subject: Added basic tests for std::begin/end and friends (Issue #252). --- tests/span_tests.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 55610c7..5e71410 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -872,6 +872,19 @@ SUITE(span_tests) TEST(begin_end) { + { + int a[] = { 1, 2, 3, 4 }; + span s = a; + + span::iterator it = s.begin(); + span::iterator it2 = std::begin(s); + CHECK(it == it2); + + it = s.end(); + it2 = std::end(s); + CHECK(it == it2); + } + { int a[] = { 1, 2, 3, 4 }; span s = a; @@ -916,6 +929,19 @@ SUITE(span_tests) TEST(cbegin_cend) { + { + int a[] = { 1, 2, 3, 4 }; + span s = a; + + span::const_iterator cit = s.cbegin(); + span::const_iterator cit2 = std::cbegin(s); + CHECK(cit == cit2); + + cit = s.cend(); + cit2 = std::cend(s); + CHECK(cit == cit2); + } + { int a[] = {1, 2, 3, 4}; span s = a; -- cgit v1.2.3 From f6cc5798a17daccf806bbc402a67c92d1a205091 Mon Sep 17 00:00:00 2001 From: galik Date: Tue, 9 Aug 2016 15:04:58 +0100 Subject: Renamed include/ folder to gsl/ to make including the library consistent whether using it from the development folder, from the installation folder or from being copied into a project. #include Updated headers/tests/instructions/cmake build accordingly This PR should address https://github.com/Microsoft/GSL/issues/277 (less the renaming of gsl itself) --- CMakeLists.txt | 8 +- README.md | 25 +- gsl/gsl.h | 172 ++++ gsl/gsl_assert.h | 77 ++ gsl/gsl_byte.h | 125 +++ gsl/gsl_util.h | 177 ++++ gsl/multi_span.h | 2239 ++++++++++++++++++++++++++++++++++++++++++ gsl/span.h | 826 ++++++++++++++++ gsl/string_span.h | 828 ++++++++++++++++ include/gsl.h | 172 ---- include/gsl_assert.h | 77 -- include/gsl_byte.h | 125 --- include/gsl_util.h | 177 ---- include/multi_span.h | 2239 ------------------------------------------ include/span.h | 826 ---------------- include/string_span.h | 828 ---------------- tests/CMakeLists.txt | 4 +- tests/assertion_tests.cpp | 2 +- tests/at_tests.cpp | 2 +- tests/bounds_tests.cpp | 2 +- tests/byte_tests.cpp | 2 +- tests/multi_span_tests.cpp | 2 +- tests/notnull_tests.cpp | 2 +- tests/owner_tests.cpp | 2 +- tests/span_tests.cpp | 2 +- tests/strided_span_tests.cpp | 2 +- tests/string_span_tests.cpp | 2 +- tests/utils_tests.cpp | 2 +- 28 files changed, 4481 insertions(+), 4466 deletions(-) create mode 100644 gsl/gsl.h create mode 100644 gsl/gsl_assert.h create mode 100644 gsl/gsl_byte.h create mode 100644 gsl/gsl_util.h create mode 100644 gsl/multi_span.h create mode 100644 gsl/span.h create mode 100644 gsl/string_span.h delete mode 100644 include/gsl.h delete mode 100644 include/gsl_assert.h delete mode 100644 include/gsl_byte.h delete mode 100644 include/gsl_util.h delete mode 100644 include/multi_span.h delete mode 100644 include/span.h delete mode 100644 include/string_span.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7397fe2..ba85857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 2.8.7) project(GSL CXX) set(GSL_HEADERS - "include/gsl.h" - "include/gsl_assert.h" - "include/span.h" - "include/string_span.h" + "gsl/gsl.h" + "gsl/gsl_assert.h" + "gsl/span.h" + "gsl/string_span.h" ) include_directories( diff --git a/README.md b/README.md index 3c440c2..f54b0c5 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ This repo contains Microsoft's implementation of GSL. The library includes types like `span`, `string_span`, `owner<>` and others. -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. +The entire implementation is provided inline in the headers under the [gsl](./gsl) 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/span.h](./include/span.h)), -it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library. +While some types have been broken out into their own headers (e.g. [gsl/span.h](./gsl/span.h)), +it is simplest to just include [gsl/gsl.h](./gsl/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 other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. @@ -66,5 +66,20 @@ All tests should pass - indicating your platform is fully supported and you are ## Using the libraries As the types are entirely implemented inline in headers, there are no linking requirements. -Just place the contents of the [include](./include) directory within your source tree so it is available -to your compiler, then include the appropriate headers in your program, and away you go! +You can copy the [gsl](./gsl) directory into your source tree so it is available +to your compiler, then include the appropriate headers in your program. + +Alternatively set your compiler's *include path* flag to point to the GSL development folder (`c:\GSL` in the example above) or installation folder (after running the install). Eg. + +MSVC++ + + /I c:\GSL + +GCC/clang + + -I$HOME/dev/GSL + + +Include the library using: + + #include diff --git a/gsl/gsl.h b/gsl/gsl.h new file mode 100644 index 0000000..8e00a44 --- /dev/null +++ b/gsl/gsl.h @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_GSL_H +#define GSL_GSL_H + +#include "gsl_assert.h" // Ensures/Expects +#include "gsl_util.h" // finally()/narrow()/narrow_cast()... +#include "multi_span.h" // multi_span, strided_span... +#include "span.h" // span +#include "string_span.h" // zstring, string_span, zstring_builder... +#include + +#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.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +template +using owner = T; + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from U* fails with nullptr +// - allow implicit conversion to U* +// +template +class not_null +{ + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); + +public: + not_null(T t) : ptr_(t) { ensure_invariant(); } + not_null& operator=(const T& t) + { + ptr_ = t; + ensure_invariant(); + return *this; + } + + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; + + template ::value>> + not_null(const not_null& other) + { + *this = other; + } + + template ::value>> + not_null& operator=(const not_null& other) + { + ptr_ = other.get(); + return *this; + } + + // prevents compilation when someone attempts to assign a nullptr + not_null(std::nullptr_t) = delete; + not_null(int) = delete; + not_null& operator=(std::nullptr_t) = delete; + not_null& operator=(int) = delete; + + T get() const + { +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } // the assume() should help the optimizer + + operator T() const { return get(); } + T operator->() const { return get(); } + + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } +private: + T ptr_; + + // 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 { Expects(ptr_ != nullptr); } + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+(size_t) = delete; + not_null& operator+=(size_t) = delete; + not_null& operator-(size_t) = delete; + not_null& operator-=(size_t) = delete; +}; + +} // namespace gsl + +namespace std +{ +template +struct hash> +{ + size_t operator()(const gsl::not_null& value) const { return hash{}(value); } +}; + +} // namespace std + +#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_GSL_H diff --git a/gsl/gsl_assert.h b/gsl/gsl_assert.h new file mode 100644 index 0000000..10de31a --- /dev/null +++ b/gsl/gsl_assert.h @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +// +// 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 +// + +namespace gsl +{ +struct fail_fast : public std::runtime_error +{ + explicit fail_fast(char const* const message) : std::runtime_error(message) {} +}; +} + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#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 diff --git a/gsl/gsl_byte.h b/gsl/gsl_byte.h new file mode 100644 index 0000000..5a9c327 --- /dev/null +++ b/gsl/gsl_byte.h @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_BYTE_H +#define GSL_BYTE_H + +#ifdef _MSC_VER + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 + +// constexpr is not understood +#pragma push_macro("constexpr") +#define constexpr + +// noexcept is not understood +#pragma push_macro("noexcept") +#define noexcept + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +namespace gsl +{ +// This is a simple definition for now that allows +// use of byte within span<> to be standards-compliant +enum class byte : unsigned char +{ +}; + +template ::value>> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte operator<<(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) >> shift); +} + +template ::value>> +constexpr byte operator>>(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) >> shift); +} + +constexpr byte& operator|=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) | static_cast(r)); +} + +constexpr byte operator|(byte l, byte r) noexcept +{ + return byte(static_cast(l) + static_cast(r)); +} + +constexpr byte& operator&=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) & static_cast(r)); +} + +constexpr byte operator&(byte l, byte r) noexcept +{ + return byte(static_cast(l) & static_cast(r)); +} + +constexpr byte& operator^=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator^(byte l, byte r) noexcept +{ + return byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } + +template ::value>> +constexpr IntegerType to_integer(byte b) noexcept +{ + return {b}; +} + +} // namespace gsl + +#ifdef _MSC_VER + +#if _MSC_VER <= 1800 + +#undef constexpr +#pragma pop_macro("constexpr") + +#undef noexcept +#pragma pop_macro("noexcept") + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#endif // GSL_BYTE_H \ No newline at end of file diff --git a/gsl/gsl_util.h b/gsl/gsl_util.h new file mode 100644 index 0000000..92a795b --- /dev/null +++ b/gsl/gsl_util.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// 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 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 +inline final_act finally(const F& f) noexcept +{ + return final_act(f); +} + +template +inline final_act finally(F&& f) noexcept +{ + return final_act(std::forward(f)); +} + +// narrow_cast(): a searchable way to do narrowing casts of values +template +inline constexpr T narrow_cast(U u) noexcept +{ + return static_cast(u); +} + +struct narrowing_error : public std::exception +{ +}; + +namespace details +{ + template + struct is_same_signedness + : public std::integral_constant::value == std::is_signed::value> + { + }; +} + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +inline T narrow(U u) +{ + T t = narrow_cast(u); + if (static_cast(t) != u) throw narrowing_error(); + if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) + throw narrowing_error(); + return t; +} + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +constexpr T& at(T (&arr)[N], size_t index) +{ + Expects(index < N); + return arr[index]; +} + +template +constexpr T& at(std::array& arr, size_t index) +{ + Expects(index < N); + return arr[index]; +} + +template +constexpr typename Cont::value_type& at(Cont& cont, size_t index) +{ + Expects(index < cont.size()); + return cont[index]; +} + +template +constexpr const T& at(std::initializer_list cont, size_t index) +{ + Expects(index < cont.size()); + return *(cont.begin() + index); +} + +} // namespace gsl + +#ifdef _MSC_VER + +#pragma warning(pop) + +#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 diff --git a/gsl/multi_span.h b/gsl/multi_span.h new file mode 100644 index 0000000..c883fb0 --- /dev/null +++ b/gsl/multi_span.h @@ -0,0 +1,2239 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_MULTI_SPAN_H +#define GSL_MULTI_SPAN_H + +#include "gsl_assert.h" +#include "gsl_byte.h" +#include "gsl_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + template + class are_integral : public std::integral_constant + { + }; + + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; +} + +template +class index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class index; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr index() noexcept {} + + constexpr index(const value_type (&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template < + typename T, typename... Ts, + typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) + : index({narrow_cast(t), narrow_cast(ds)...}) + { + } +#else + template ::value>> + constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + { + } +#endif + + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } + + constexpr index operator+() const noexcept { return *this; } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return narrow_cast(-1); + } + + template ::value>> + constexpr bool operator==(T other) const noexcept + { + return narrow_cast(-1) == other; + } + + template ::value>> + constexpr bool operator!=(T other) const noexcept + { + return narrow_cast(-1) != other; + } +}; + +template ::value>> +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges + { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + { + } + + BoundsRanges(const BoundsRanges&) = default; + BoundsRanges& operator=(const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) {} + BoundsRanges() = default; + + template + void serialize(T&) const + { + } + + template + size_type linearize(const T&) const + { + return 0; + } + + template + size_type contains(const T&) const + { + return -1; + } + + size_type elementNum(size_t) const noexcept { return 0; } + + size_type totalSize() const noexcept { return TotalSize; } + + bool operator==(const BoundsRanges&) const noexcept { return true; } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + Expects(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + Expects(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept { return m_bound; } + + size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + Expects(arr[Dim] < CurrentRange); // Index is out of range + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } + + size_type elementNum() const noexcept { return CurrentRange; } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; + + template + struct TypeListIndexer + { + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) + { + return obj_; + } + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) + { + return TypeListIndexer(static_cast(obj_)).template get(); + } + + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain& obj) + { + return TypeListIndexer(obj); + } + + template 1), + typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) {} +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + + template + friend class static_bounds; + +public: + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(static_cast(il.begin())) + { + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new (&m_ranges) MyRanges(otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast&>(m_ranges)}; + } + + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return {extents}; + } + + template + constexpr bool operator==(const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator!=(const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds&) noexcept = default; + + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; + + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + { + } + + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), + m_strides(strides) + { + } + + constexpr index_type strides() const noexcept { return m_strides; } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const noexcept + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) { + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const noexcept { return m_strides[0]; } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return {details::shift_left(m_extents), details::shift_left(m_strides)}; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const noexcept { return m_extents; } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; + +template +class bounds_iterator : public std::iterator +{ +private: + using Base = std::iterator; + +public: + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept { return curr_; } + + constexpr pointer operator->() const noexcept { return &curr_; } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) { + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; + return *this; + } + curr_[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr_ = boundary_; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr_, boundary_)) { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { + curr_[i] = boundary_[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) { + if (curr_[i] >= 1) { + curr_[i]--; + return *this; + } + curr_[i] = boundary_[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + Expects(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr_) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) { + stride[i] = stride[i + 1] * boundary_[i + 1]; + } + for (size_t i = 0; i < rank; ++i) { + curr_[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + // index is out of bounds of the array + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr_) - linearize(rhs.curr_); + } + + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr_ == rhs.curr_; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr_, rhs.curr_); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); + } + +private: + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary_)) { + res = 1; + for (size_t i = rank; i-- > 0;) { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary_[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) { + res += idx[i] * multiplier; + multiplier *= boundary_[i]; + } + } + return res; + } + + value_type boundary_; + std::remove_const_t curr_; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) noexcept +{ + return rhs + n; +} + +namespace details +{ + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return {stride}; + } + + template + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + static_assert(is_bounds::value && is_bounds::value, + "The src type and dest type must be bounds"); + static_assert(std::is_same::value, + "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || + BoundsSrc::static_size == dynamic_range || + BoundsDest::static_size == BoundsSrc::static_size, + "The source bounds must have same size as dest bounds"); + Expects(src.size() == dest.size()); + } + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; + +template +struct dim_t +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim_t +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim_t(std::ptrdiff_t size) : dvalue(size) {} +}; + +template +constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { + return dim_t(); +} + +template +constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { + return dim_t<>(n); +} + +template +class multi_span; + +template +class strided_span; + +namespace details +{ + template + struct SpanTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct SpanTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct SpanArrayTraits + { + using type = multi_span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct SpanArrayTraits : SpanArrayTraits + { + }; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + Expects(BoundsType::static_size <= totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); + } + + struct Sep + { + }; + + template + T static_as_multi_span_helper(Sep, Args... args) + { + return T{narrow_cast(args)...}; + } + template + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_multi_span_helper(Arg, Args... args) + { + return static_as_multi_span_helper(args...); + } + template + T static_as_multi_span_helper(dim_t val, Args... args) + { + return static_as_multi_span_helper(args..., val.dvalue); + } + + template + struct static_as_multi_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_multi_span_oracle : std::false_type + { + }; + + template + struct is_multi_span_oracle> + : std::true_type + { + }; + + template + struct is_multi_span_oracle> : std::true_type + { + }; + + template + struct is_multi_span : is_multi_span_oracle> + { + }; +} + +template +class multi_span +{ + // TODO do we still need this? + template + friend class multi_span; + +public: + using bounds_type = static_bounds; + static const size_t Rank = bounds_type::rank; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = contiguous_span_iterator; + using const_span = multi_span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + +public: + // default constructor - same as constructing from nullptr_t + constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr - get an empty multi_span + constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + Expects(size == 0); + } + + // construct from a single element + constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } + + // prevent constructing from temporaries for single-elements + constexpr multi_span(value_type&&) = delete; + + // construct from pointer + length + constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) + { + } + + // construct from pointer + length - multidimensional + constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + } + + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr multi_span(pointer begin, Ptr end) + : multi_span(begin, + details::newBoundsHelper(static_cast(end) - begin)) + { + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + } + + // construct from n-dimensions static array + template > + constexpr multi_span(T (&arr)[N]) + : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot construct a multi_span from an array with fewer elements."); + } + + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr multi_span(T* const& data, size_type size) + : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + } + + // construct from std::array + template + constexpr multi_span(std::array& arr) + : multi_span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert( + std::is_convertible(*)[]>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // construct from const std::array + template + constexpr multi_span(const std::array, N>& arr) + : multi_span(arr.data(), static_bounds()) + { + static_assert(std::is_convertible>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // prevent constructing from temporary std::array + template + constexpr multi_span(std::array&& arr) = delete; + + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr multi_span(Cont& cont) + : multi_span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) + { + } + + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr multi_span(Cont&& cont) = delete; + + // construct from a convertible multi_span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr multi_span(multi_span other) noexcept + : data_(other.data_), + bounds_(other.bounds_) + { + } + +// trivial copy and move +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span(multi_span&&) = default; +#endif + constexpr multi_span(const multi_span&) = default; + +// trivial assignment +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span& operator=(multi_span&&) = default; +#endif + constexpr multi_span& operator=(const multi_span&) = default; + + // first() - extract the first Count elements into a new multi_span + template + constexpr multi_span first() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; + } + + // first() - extract the first count elements into a new multi_span + constexpr multi_span first(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; + } + + // last() - extract the last Count elements into a new multi_span + template + constexpr multi_span last() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; + } + + // last() - extract the last count elements into a new multi_span + constexpr multi_span last(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; + } + + // subspan() - create a subview of Count elements starting at Offset + template + constexpr multi_span subspan() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the multi_span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; + } + + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr multi_span subspan(size_type offset, + size_type count = dynamic_range) const + noexcept + { + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; + } + + // section - creates a non-contiguous, strided multi_span from a contiguous one + constexpr strided_span section(index_type origin, index_type extents) const + noexcept + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; + } + + // length of the multi_span in elements + constexpr size_type size() const noexcept { return bounds_.size(); } + + // length of the multi_span in elements + constexpr size_type length() const noexcept { return this->size(); } + + // length of the multi_span in bytes + constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } + + // length of the multi_span in bytes + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } + + constexpr bool empty() const noexcept { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + return bounds_.extent(dim); + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + constexpr pointer data() const noexcept { return data_; } + + template + constexpr reference operator()(FirstIndex index) + { + return this->operator[](narrow_cast(index)); + } + + template + constexpr reference operator()(FirstIndex index, OtherIndices... indices) + { + index_type idx = {narrow_cast(index), + narrow_cast(indices...)}; + return this->operator[](idx); + } + + constexpr reference operator[](const index_type& idx) const noexcept + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const noexcept + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; + } + + constexpr iterator begin() const noexcept { return iterator{this, true}; } + + constexpr iterator end() const noexcept { return iterator{this, false}; } + + constexpr const_iterator cbegin() const noexcept + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const noexcept + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator{cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const multi_span& other) const + noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const multi_span& other) const + noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const multi_span& other) const + noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const multi_span& other) const + noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const multi_span& other) const + noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const multi_span& other) const + noexcept + { + return !(*this < other); + } +}; + +// +// Free functions for manipulating spans +// + +// reshape a multi_span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr auto as_multi_span(SpanType s, Dimensions2... dims) + -> multi_span +{ + static_assert(details::is_multi_span::value, + "Variadic as_multi_span() is for reshaping existing spans."); + using BoundsType = + typename multi_span::bounds_type; + auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a multi_span to a multi_span +template +multi_span as_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span (a writeable byte multi_span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +multi_span as_writeable_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_multi_span(multi_span s) noexcept -> multi_span< + const U, static_cast( + multi_span::bounds_type::static_size != dynamic_range + ? (static_cast( + multi_span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_multi_span(multi_span s) noexcept + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range + ? static_cast( + multi_span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +template +constexpr auto as_multi_span(T* const& ptr, dim_t... args) + -> multi_span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), + details::static_as_multi_span_helper>(args..., + details::Sep{})}; +} + +template +constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array&&) = delete; + +template +constexpr multi_span as_multi_span(std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(T* begin, T* end) +{ + return {begin, end}; +} + +template +constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> +{ + Expects(arr.size() < PTRDIFF_MAX); + return {arr.data(), narrow_cast(arr.size())}; +} + +template +constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> = delete; + +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_multi_span(std::basic_string& str) + -> multi_span +{ + Expects(str.size() < PTRDIFF_MAX); + return {&str[0], narrow_cast(str.size())}; +} + +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. +template +class strided_span +{ +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + template + friend class strided_span; + +public: + // from raw data + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : data_(ptr), bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void) size; + } + + // from static array of size N + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } + + // from array view + template ::value, + typename Dummy = std::enable_if_t> + constexpr strided_span(multi_span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + + // convertible + template ::value>> + constexpr strided_span(const strided_span& other) + : data_(other.data_), bounds_(other.bounds_) + { + } + + // convert from bytes + template + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return {const_cast(reinterpret_cast(this->data())), + size, bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; + } + + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); + } + + constexpr size_type size() const noexcept { return bounds_.size(); } + + constexpr pointer data() const noexcept { return data_; } + + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } + + constexpr iterator begin() const { return iterator{this, true}; } + + constexpr iterator end() const { return iterator{this, false}; } + + constexpr const_iterator cbegin() const + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } + + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const noexcept + { + return !(*this < other); + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + // The last dimension of the array needs to contain a multiple of new type elements + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); + + index_type ret = extent; + ret[Rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + + for (size_t i = Rank - 1; i > 0; --i) { + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + } + + index_type ret = strides / d; + ret[Rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + +private: + template + friend class multi_span; + + pointer data_; + const Span* m_validator; + void validateThis() const + { + // iterator is out of range of the array + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + } + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + +public: + reference operator*() const noexcept + { + validateThis(); + return *data_; + } + pointer operator->() const noexcept + { + validateThis(); + return data_; + } + contiguous_span_iterator& operator++() noexcept + { + ++data_; + return *this; + } + contiguous_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() noexcept + { + --data_; + return *this; + } + contiguous_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) noexcept + { + data_ += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ - rhs.data_; + } + reference operator[](difference_type n) const noexcept { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ == rhs.data_; + } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ < rhs.data_; + } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(contiguous_span_iterator& rhs) noexcept + { + std::swap(data_, rhs.data_); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +class general_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + +private: + template + friend class strided_span; + + const Span* m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } + +public: + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + difference_type operator-(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; } + + bool operator==(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(general_span_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +} // namespace gsl + +#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_VARIADIC_CTOR_BUG + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#endif // GSL_MULTI_SPAN_H diff --git a/gsl/span.h b/gsl/span.h new file mode 100644 index 0000000..1405aa9 --- /dev/null +++ b/gsl/span.h @@ -0,0 +1,826 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include "gsl_assert.h" +#include "gsl_byte.h" +#include "gsl_util.h" +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR +#define GSL_MSVC_NO_CPP14_STD_EQUAL + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +#pragma push_macro("alignof") +#define alignof __alignof + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + +// [views.constants], constants +constexpr const std::ptrdiff_t dynamic_extent = -1; + +template +class span; + +// implementation details +namespace details +{ + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : public is_std_array_oracle> + { + }; + + template + struct is_allowed_pointer_conversion + : public std::integral_constant::value && + std::is_pointer::value && + std::is_convertible::value> + { + }; + + template + struct is_allowed_integral_conversion + : public std::integral_constant< + bool, std::is_integral::value && std::is_integral::value && + sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && + std::is_convertible::value> + { + }; + + template + struct is_allowed_extent_conversion + : public std::integral_constant + { + }; + + template + struct is_allowed_element_type_conversion + : public std::integral_constant>::value || + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value> + { + }; + + template + struct is_allowed_element_type_conversion + : public std::integral_constant::value> + { + }; + + template + struct is_allowed_element_type_conversion : public std::true_type + { + }; + + template + class const_span_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using const_pointer = std::add_const_t; + using pointer = const_pointer; + + using const_reference = std::add_const_t; + using reference = const_reference; + + constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} + constexpr const_span_iterator(const Span* span, typename Span::index_type index) + : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } + + constexpr reference operator*() const + { + Expects(span_); + return (*span_)[index_]; + } + + constexpr pointer operator->() const + { + Expects(span_); + return &((*span_)[index_]); + } + + constexpr const_span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } + + constexpr const_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr const_span_iterator& operator--() noexcept + { + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; + return *this; + } + + constexpr const_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr const_span_iterator operator+(difference_type n) const noexcept + { + auto ret = *this; + return ret += n; + } + + constexpr const_span_iterator& operator+=(difference_type n) noexcept + { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; + } + + constexpr const_span_iterator operator-(difference_type n) const noexcept + { + auto ret = *this; + return ret -= n; + } + + constexpr const_span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + + constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const const_span_iterator& rhs) const noexcept + { + return span_ == rhs.span_ && index_ == rhs.index_; + } + + constexpr bool operator!=(const const_span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } + + constexpr bool operator<=(const const_span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const const_span_iterator& rhs) const noexcept + { + return rhs < *this; + } + + constexpr bool operator>=(const const_span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(const_span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(span_, rhs.span_); + } + + private: + const Span* span_; + std::ptrdiff_t index_; + }; + + template + class span_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using pointer = value_type*; + using reference = value_type&; + + constexpr span_iterator() : span_iterator(nullptr, 0) {} + constexpr span_iterator(const Span* span, typename Span::index_type index) + : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } + + constexpr reference operator*() const + { + Expects(span_); + return (*span_)[index_]; + } + + constexpr pointer operator->() const + { + Expects(span_); + return &((*span_)[index_]); + } + + constexpr span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } + + constexpr span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr span_iterator& operator--() noexcept + { + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; + return *this; + } + + constexpr span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr span_iterator operator+(difference_type n) const noexcept + { + auto ret = *this; + return ret += n; + } + + constexpr span_iterator& operator+=(difference_type n) noexcept + { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; + } + + constexpr span_iterator operator-(difference_type n) const noexcept + { + auto ret = *this; + return ret -= n; + } + + constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + + constexpr difference_type operator-(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const span_iterator& rhs) const noexcept + { + return span_ == rhs.span_ && index_ == rhs.index_; + } + + constexpr bool operator!=(const span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } + + constexpr bool operator<=(const span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(span_, rhs.span_); + } + + private: + const Span* span_; + std::ptrdiff_t index_; + }; + + template + constexpr const_span_iterator + operator+(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs + n; + } + + template + constexpr const_span_iterator + operator-(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs - n; + } + + template + constexpr span_iterator operator+(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs + n; + } + + template + constexpr span_iterator operator-(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs - n; + } + + template + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type ext) noexcept + { + static_assert(Other == Ext || Other == dynamic_extent, + "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Ext); + } + + constexpr extent_type(index_type size) { Expects(size == Ext); } + + constexpr inline index_type size() const noexcept { return Ext; } + }; + + template <> + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + { + } + + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept { return size_; } + + private: + index_type size_; + }; +} // namespace details + +// [span], class template span +template +class span +{ +public: + // constants and types + using element_type = ElementType; + using index_type = std::ptrdiff_t; + using pointer = element_type*; + using reference = element_type&; + + using iterator = details::span_iterator>; + using const_iterator = details::const_span_iterator>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr static const index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} + + constexpr span(std::nullptr_t) noexcept : span() {} + + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} + + constexpr span(pointer firstElem, pointer lastElem) + : storage_(firstElem, std::distance(firstElem, lastElem)) + { + } + + template + constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) + { + } + + template > + constexpr span(std::array& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + template + constexpr span(const std::array, N>& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement + // on Container to be a contiguous sequence container. + template ::value && !details::is_std_array::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(Container& cont) : span(cont.data(), cont.size()) + { + } + + template ::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(const Container& cont) : span(cont.data(), cont.size()) + { + } + + constexpr span(const span& other) noexcept = default; +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr span(span&& other) noexcept = default; +#else + constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {} +#endif + + template < + class OtherElementType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> + constexpr span(const span& other) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) + { + } + + template < + class OtherElementType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> + constexpr span(span&& other) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) + { + } + + ~span() noexcept = default; + constexpr span& operator=(const span& other) noexcept = default; + +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr span& operator=(span&& other) noexcept = default; +#else + constexpr span& operator=(span&& other) noexcept + { + storage_ = std::move(other.storage_); + return *this; + } +#endif + // [span.sub], span subviews + template + constexpr span first() const + { + Expects(Count >= 0 && Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const + { + Expects(Count >= 0 && Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + constexpr span subspan() const + { + Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && + (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); + return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; + } + + constexpr span first(index_type count) const + { + Expects(count >= 0 && count <= size()); + return {data(), count}; + } + + constexpr span last(index_type count) const + { + Expects(count >= 0 && count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span subspan(index_type offset, + index_type count = dynamic_extent) const + { + Expects((offset == 0 || (offset > 0 && offset <= size())) && + (count == dynamic_extent || (count >= 0 && offset + count <= size()))); + return {data() + offset, count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr index_type length() const noexcept { return size(); } + constexpr index_type size() const noexcept { return storage_.size(); } + constexpr index_type length_bytes() const noexcept { return size_bytes(); } + constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr reference operator[](index_type idx) const + { + Expects(idx >= 0 && idx < storage_.size()); + return data()[idx]; + } + constexpr reference operator()(index_type idx) const { return this->operator[](idx); } + constexpr pointer data() const noexcept { return storage_.data(); } + + // [span.iter], span iterator support + iterator begin() const noexcept { return {this, 0}; } + iterator end() const noexcept { return {this, length()}; } + + const_iterator cbegin() const noexcept { return {this, 0}; } + const_iterator cend() const noexcept { return {this, length()}; } + + reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } + +private: + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + template + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) + { + Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); + } + + constexpr inline pointer data() const noexcept { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; +}; + +// [span.comparison], span comparison operators +template +constexpr bool operator==(const span& l, + const span& r) +{ +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin()); +#else + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +#endif +} + +template +constexpr bool operator!=(const span& l, const span& r) +{ + return !(l == r); +} + +template +constexpr bool operator<(const span& l, const span& r) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator<=(const span& l, const span& r) +{ + return !(l > r); +} + +template +constexpr bool operator>(const span& l, const span& r) +{ + return r < l; +} + +template +constexpr bool operator>=(const span& l, const span& r) +{ + return !(l < r); +} + +namespace details +{ + // if we only supported compilers with good constexpr support then + // this pair of classes could collapse down to a constexpr function + + // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as + // constexpr + // and so will fail compilation of the template + template + struct calculate_byte_size + : std::integral_constant(sizeof(ElementType) * + static_cast(Extent))> + { + }; + + template + struct calculate_byte_size + : std::integral_constant + { + }; +} + +// [span.objectrep], views of object representation +template +span::value> +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +span::value> +as_writeable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +} // namespace gsl + +#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 + +#pragma pop_macro("alignof") + +#undef GSL_MSVC_HAS_VARIADIC_CTOR_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 + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // GSL_SPAN_H diff --git a/gsl/string_span.h b/gsl/string_span.h new file mode 100644 index 0000000..e18e07a --- /dev/null +++ b/gsl/string_span.h @@ -0,0 +1,828 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +#pragma warning(push) + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#define GSL_MSVC_NO_CPP14_STD_EQUAL +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +// 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 +using basic_zstring = CharT*; + +template +using czstring = basic_zstring; + +template +using cwzstring = basic_zstring; + +template +using zstring = basic_zstring; + +template +using wzstring = basic_zstring; + +// +// 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 +span 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 +inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const +// overloads to share an implementation +inline span ensure_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +template +span ensure_z(T (&sz)[N]) +{ + return ensure_z(&sz[0], static_cast(N)); +} + +template +span::type, dynamic_extent> +ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), static_cast(cont.length())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + { + }; + + template + struct is_basic_string_span_oracle> : std::true_type + { + }; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + { + }; + + template + struct length_func + { + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; +} + +// +// string_span and relatives +// +template +class basic_string_span +{ +public: + using element_type = CharT; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + using impl_type = span; + + using index_type = typename impl_type::index_type; + 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() noexcept = default; + + // copy + constexpr basic_string_span(const basic_string_span& other) noexcept = default; + +// move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span(basic_string_span&& other) noexcept = default; +#else + constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} +#endif + + // assign + constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; + +// move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; +#else + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept + { + span_ = std::move(other.span_); + return *this; + } +#endif + + // from nullptr + constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {} + + constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} + constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} + + // From static arrays - if 0-terminated, remove 0 from the view + // All other containers allow 0s within the length, so we do not remove them + template + constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) + { + } + + template > + constexpr basic_string_span(std::array& arr) noexcept : span_(arr) + { + } + + template > + constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) + { + } + + // Container signature should work for basic_string after C++17 version exists + template + constexpr basic_string_span(std::basic_string& str) + : span_(&str[0], str.length()) + { + } + + template + constexpr basic_string_span(const std::basic_string& str) + : span_(&str[0], str.length()) + { + } + + // from containers. Containers must have a pointer type and data() function signatures + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Container& cont) : span_(cont) + { + } + + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Container& cont) : span_(cont) + { + } + + // from string_span + template < + class OtherValueType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) + : span_(other.data(), other.length()) + { + } + + template + constexpr basic_string_span first() const + { + return {span_.template first()}; + } + + constexpr basic_string_span first(index_type count) const + { + return {span_.first(count)}; + } + + template + constexpr basic_string_span last() const + { + return {span_.template last()}; + } + + constexpr basic_string_span last(index_type count) const + { + return {span_.last(count)}; + } + + template + constexpr basic_string_span subspan() const + { + return {span_.template subspan()}; + } + + constexpr basic_string_span + subspan(index_type offset, index_type count = dynamic_extent) const + { + return {span_.subspan(offset, count)}; + } + + constexpr reference operator[](index_type idx) const { return span_[idx]; } + constexpr reference operator()(index_type idx) const { return span_[idx]; } + + constexpr pointer data() const { return span_.data(); } + + constexpr index_type length() const noexcept { return span_.size(); } + constexpr index_type size() const noexcept { return span_.size(); } + constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } + constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr bool empty() const noexcept { return size() == 0; } + + 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 { return 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) + { + return {sz, details::length_func()(sz, max)}; + } + + template + static impl_type remove_z(element_type (&sz)[N]) + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + +// +// to_string() allow (explicit) conversions from string_span to string +// +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +template +std::basic_string::type> +to_string(basic_string_span view) +{ + return {view.data(), static_cast(view.length())}; +} + +#else + +inline std::string to_string(cstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::string to_string(string_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::wstring to_string(cwstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::wstring to_string(wstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +#endif + +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings +template +class basic_zstring_span +{ +public: + using value_type = CharT; + using const_value_type = std::add_const_t; + + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; + + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; + + using impl_type = span; + using string_span_type = basic_string_span; + + constexpr basic_zstring_span(impl_type s) noexcept : span_(s) + { + // expects a zero-terminated span + Expects(s[s.size() - 1] == '\0'); + } + + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; + +// move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {} +#endif + + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; + +// move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + constexpr bool empty() const noexcept { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const noexcept + { + return span_.first(span_.size() - 1); + } + + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } + +private: + impl_type span_; +}; + +template +using zstring_span = basic_zstring_span; + +template +using wzstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +// operator == +template ::value || + std::is_convertible>>::value>> +bool operator==(const gsl::basic_string_span& one, const T& other) noexcept +{ + gsl::basic_string_span> tmp(other); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); +#else + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +#endif +} + +template ::value && + std::is_convertible>>::value>> +bool operator==(const T& one, const gsl::basic_string_span& other) noexcept +{ + gsl::basic_string_span> tmp(one); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); +#else + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +#endif +} + +// operator != +template , Extent>>::value>> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} + +// operator< +template , Extent>>::value>> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + +// operator <= +template , Extent>>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<=(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} +#endif + +// operator> +template , Extent>>::value>> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} +#endif + +// operator >= +template , Extent>>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>=(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} +#endif +} // namespace GSL + +#ifdef _MSC_VER + +#pragma warning(pop) + +#undef constexpr +#pragma pop_macro("constexpr") + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#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 +#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#undef GSL_MSVC_NO_CPP14_STD_EQUAL +#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +#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 diff --git a/include/gsl.h b/include/gsl.h deleted file mode 100644 index 8e00a44..0000000 --- a/include/gsl.h +++ /dev/null @@ -1,172 +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_GSL_H -#define GSL_GSL_H - -#include "gsl_assert.h" // Ensures/Expects -#include "gsl_util.h" // finally()/narrow()/narrow_cast()... -#include "multi_span.h" // multi_span, strided_span... -#include "span.h" // span -#include "string_span.h" // zstring, string_span, zstring_builder... -#include - -#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.owner: ownership pointers -// -using std::unique_ptr; -using std::shared_ptr; - -template -using owner = T; - -// -// not_null -// -// Restricts a pointer or smart pointer to only hold non-null values. -// -// Has zero size overhead over T. -// -// If T is a pointer (i.e. T == U*) then -// - allow construction from U* or U& -// - disallow construction from nullptr_t -// - disallow default construction -// - ensure construction from U* fails with nullptr -// - allow implicit conversion to U* -// -template -class not_null -{ - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); - -public: - not_null(T t) : ptr_(t) { ensure_invariant(); } - not_null& operator=(const T& t) - { - ptr_ = t; - ensure_invariant(); - return *this; - } - - not_null(const not_null& other) = default; - not_null& operator=(const not_null& other) = default; - - template ::value>> - not_null(const not_null& other) - { - *this = other; - } - - template ::value>> - not_null& operator=(const not_null& other) - { - ptr_ = other.get(); - return *this; - } - - // prevents compilation when someone attempts to assign a nullptr - not_null(std::nullptr_t) = delete; - not_null(int) = delete; - not_null& operator=(std::nullptr_t) = delete; - not_null& operator=(int) = delete; - - T get() const - { -#ifdef _MSC_VER - __assume(ptr_ != nullptr); -#endif - return ptr_; - } // the assume() should help the optimizer - - operator T() const { return get(); } - T operator->() const { return get(); } - - bool operator==(const T& rhs) const { return ptr_ == rhs; } - bool operator!=(const T& rhs) const { return !(*this == rhs); } -private: - T ptr_; - - // 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 { Expects(ptr_ != nullptr); } - - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - not_null& operator++() = delete; - not_null& operator--() = delete; - not_null operator++(int) = delete; - not_null operator--(int) = delete; - not_null& operator+(size_t) = delete; - not_null& operator+=(size_t) = delete; - not_null& operator-(size_t) = delete; - not_null& operator-=(size_t) = delete; -}; - -} // namespace gsl - -namespace std -{ -template -struct hash> -{ - size_t operator()(const gsl::not_null& value) const { return hash{}(value); } -}; - -} // namespace std - -#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_GSL_H diff --git a/include/gsl_assert.h b/include/gsl_assert.h deleted file mode 100644 index 10de31a..0000000 --- a/include/gsl_assert.h +++ /dev/null @@ -1,77 +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_CONTRACTS_H -#define GSL_CONTRACTS_H - -#include -#include - -// -// 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 -// - -namespace gsl -{ -struct fail_fast : public std::runtime_error -{ - explicit fail_fast(char const* const message) : std::runtime_error(message) {} -}; -} - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#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 diff --git a/include/gsl_byte.h b/include/gsl_byte.h deleted file mode 100644 index 5a9c327..0000000 --- a/include/gsl_byte.h +++ /dev/null @@ -1,125 +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_BYTE_H -#define GSL_BYTE_H - -#ifdef _MSC_VER - -// MSVC 2013 workarounds -#if _MSC_VER <= 1800 - -// constexpr is not understood -#pragma push_macro("constexpr") -#define constexpr - -// noexcept is not understood -#pragma push_macro("noexcept") -#define noexcept - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -namespace gsl -{ -// This is a simple definition for now that allows -// use of byte within span<> to be standards-compliant -enum class byte : unsigned char -{ -}; - -template ::value>> -constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte operator<<(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) >> shift); -} - -template ::value>> -constexpr byte operator>>(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) >> shift); -} - -constexpr byte& operator|=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) | static_cast(r)); -} - -constexpr byte operator|(byte l, byte r) noexcept -{ - return byte(static_cast(l) + static_cast(r)); -} - -constexpr byte& operator&=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) & static_cast(r)); -} - -constexpr byte operator&(byte l, byte r) noexcept -{ - return byte(static_cast(l) & static_cast(r)); -} - -constexpr byte& operator^=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator^(byte l, byte r) noexcept -{ - return byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } - -template ::value>> -constexpr IntegerType to_integer(byte b) noexcept -{ - return {b}; -} - -} // namespace gsl - -#ifdef _MSC_VER - -#if _MSC_VER <= 1800 - -#undef constexpr -#pragma pop_macro("constexpr") - -#undef noexcept -#pragma pop_macro("noexcept") - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#endif // GSL_BYTE_H \ No newline at end of file diff --git a/include/gsl_util.h b/include/gsl_util.h deleted file mode 100644 index 92a795b..0000000 --- a/include/gsl_util.h +++ /dev/null @@ -1,177 +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_UTIL_H -#define GSL_UTIL_H - -#include "gsl_assert.h" // Ensures/Expects -#include -#include -#include -#include - -#ifdef _MSC_VER - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -// 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 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 -inline final_act finally(const F& f) noexcept -{ - return final_act(f); -} - -template -inline final_act finally(F&& f) noexcept -{ - return final_act(std::forward(f)); -} - -// narrow_cast(): a searchable way to do narrowing casts of values -template -inline constexpr T narrow_cast(U u) noexcept -{ - return static_cast(u); -} - -struct narrowing_error : public std::exception -{ -}; - -namespace details -{ - template - struct is_same_signedness - : public std::integral_constant::value == std::is_signed::value> - { - }; -} - -// narrow() : a checked version of narrow_cast() that throws if the cast changed the value -template -inline T narrow(U u) -{ - T t = narrow_cast(u); - if (static_cast(t) != u) throw narrowing_error(); - if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) - throw narrowing_error(); - return t; -} - -// -// at() - Bounds-checked way of accessing static arrays, std::array, std::vector -// -template -constexpr T& at(T (&arr)[N], size_t index) -{ - Expects(index < N); - return arr[index]; -} - -template -constexpr T& at(std::array& arr, size_t index) -{ - Expects(index < N); - return arr[index]; -} - -template -constexpr typename Cont::value_type& at(Cont& cont, size_t index) -{ - Expects(index < cont.size()); - return cont[index]; -} - -template -constexpr const T& at(std::initializer_list cont, size_t index) -{ - Expects(index < cont.size()); - return *(cont.begin() + index); -} - -} // namespace gsl - -#ifdef _MSC_VER - -#pragma warning(pop) - -#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 diff --git a/include/multi_span.h b/include/multi_span.h deleted file mode 100644 index c883fb0..0000000 --- a/include/multi_span.h +++ /dev/null @@ -1,2239 +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_MULTI_SPAN_H -#define GSL_MULTI_SPAN_H - -#include "gsl_assert.h" -#include "gsl_byte.h" -#include "gsl_util.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - -// noexcept is not understood -#ifndef GSL_THROW_ON_CONTRACT_VIOLATION -#pragma push_macro("noexcept") -#define noexcept /* nothing */ -#endif - -// turn off some misguided warnings -#pragma warning(push) -#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable : 4512) // warns that assignment op could not be generated - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#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 -{ - -/* -** begin definitions of index and bounds -*/ -namespace details -{ - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; - - template - class are_integral : public std::integral_constant - { - }; - - template - class are_integral - : public std::integral_constant::value && are_integral::value> - { - }; -} - -template -class index final -{ - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class index; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr index() noexcept {} - - constexpr index(const value_type (&values)[Rank]) noexcept - { - std::copy(values, values + Rank, elems); - } - -#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template < - typename T, typename... Ts, - typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) - : index({narrow_cast(t), narrow_cast(ds)...}) - { - } -#else - template ::value>> - constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} - { - } -#endif - - constexpr index(const index& other) noexcept = default; - - constexpr index& operator=(const index& rhs) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const noexcept - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - constexpr bool operator==(const index& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } - - constexpr index operator+() const noexcept { return *this; } - - constexpr index operator-() const noexcept - { - index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr index operator+(const index& rhs) const noexcept - { - index ret = *this; - ret += rhs; - return ret; - } - - constexpr index operator-(const index& rhs) const noexcept - { - index ret = *this; - ret -= rhs; - return ret; - } - - constexpr index& operator+=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr index operator*(value_type v) const noexcept - { - index ret = *this; - ret *= v; - return ret; - } - - constexpr index operator/(value_type v) const noexcept - { - index ret = *this; - ret /= v; - return ret; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } - - constexpr index& operator*=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } - -private: - value_type elems[Rank] = {}; -}; - -#ifndef _MSC_VER - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const noexcept - { - return narrow_cast(-1); - } - - template ::value>> - constexpr bool operator==(T other) const noexcept - { - return narrow_cast(-1) == other; - } - - template ::value>> - constexpr bool operator!=(T other) const noexcept - { - return narrow_cast(-1) != other; - } -}; - -template ::value>> -constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right == left; -} - -template ::value>> -constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const std::ptrdiff_t dynamic_range = -1; -#endif - -struct generalized_mapping_tag -{ -}; -struct contiguous_mapping_tag : generalized_mapping_tag -{ -}; - -namespace details -{ - - template - struct LessThan - { - static const bool value = Left < Right; - }; - - template - struct BoundsRanges - { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; - - // TODO : following signature is for work around VS bug - template - BoundsRanges(const OtherRange&, bool /* firstLevel */) - { - } - - BoundsRanges(const BoundsRanges&) = default; - BoundsRanges& operator=(const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) {} - BoundsRanges() = default; - - template - void serialize(T&) const - { - } - - template - size_type linearize(const T&) const - { - return 0; - } - - template - size_type contains(const T&) const - { - return -1; - } - - size_type elementNum(size_t) const noexcept { return 0; } - - size_type totalSize() const noexcept { return TotalSize; } - - bool operator==(const BoundsRanges&) const noexcept { return true; } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) - : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - Expects(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, - bool /* firstLevel */ = true) - : Base(static_cast&>(other), false) - , m_bound(other.totalSize()) - { - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - const size_type index = this->Base::totalSize() * arr[Dim]; - Expects(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const noexcept { return m_bound; } - - size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return m_bound == rhs.m_bound && - static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = - Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges& other, - bool firstLevel = true) - : Base(static_cast&>(other), false) - { - (void) firstLevel; - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - Expects(arr[Dim] < CurrentRange); // Index is out of range - return this->Base::totalSize() * arr[Dim] + - this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - if (arr[Dim] >= CurrentRange) return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } - - size_type elementNum() const noexcept { return CurrentRange; } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible - : public std::integral_constant= TargetType::TotalSize || - TargetType::TotalSize == dynamic_range || - SourceType::TotalSize == dynamic_range || - TargetType::TotalSize == 0)> - { - }; - - template - struct TypeListIndexer - { - const TypeChain& obj_; - TypeListIndexer(const TypeChain& obj) : obj_(obj) {} - - template - const TypeChain& getObj(std::true_type) - { - return obj_; - } - - template - auto getObj(std::false_type) - -> decltype(TypeListIndexer(static_cast(obj_)).template get()) - { - return TypeListIndexer(static_cast(obj_)).template get(); - } - - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain& obj) - { - return TypeListIndexer(obj); - } - - template 1), - typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) { - ret[i] = other[i + 1]; - } - return ret; - } -} - -template -class bounds_iterator; - -template -class static_bounds -{ -public: - static_bounds(const details::BoundsRanges&) {} -}; - -template -class static_bounds -{ - using MyRanges = details::BoundsRanges; - - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} - - template - friend class static_bounds; - -public: - static const size_t rank = MyRanges::Depth; - static const size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; - - using size_type = std::ptrdiff_t; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; - - constexpr static_bounds(const static_bounds&) = default; - - template - struct BoundsRangeConvertible2; - - template > - static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant())) - { - }; - - template - struct BoundsRangeConvertible2 : std::true_type - { - }; - - template - struct BoundsRangeConvertible - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant::value || - TargetType::CurrentRange == dynamic_range || - SourceType::CurrentRange == dynamic_range)>())) - { - }; - - template - struct BoundsRangeConvertible : std::true_type - { - }; - - template , - details::BoundsRanges>::value>> - constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - { - Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || - MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); - } - - constexpr static_bounds(std::initializer_list il) - : m_ranges(static_cast(il.begin())) - { - // Size of the initializer list must match the rank of the array - Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || - MyRanges::DynamicNum == il.size()); - // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); - } - - constexpr static_bounds() = default; - - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new (&m_ranges) MyRanges(otherBounds.m_ranges); - return *this; - } - - constexpr sliced_type slice() const noexcept - { - return sliced_type{static_cast&>(m_ranges)}; - } - - constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } - - constexpr size_type size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - - constexpr bool contains(const index_type& idx) const noexcept - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](size_t index) const noexcept - { - return m_ranges.elementNum(index); - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, - "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - static_assert(std::is_integral::value, - "Dimension parameter must be supplied as an integral type."); - auto real_dim = narrow_cast(dim); - Expects(real_dim < rank); - - return m_ranges.elementNum(real_dim); - } - - constexpr index_type index_bounds() const noexcept - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return {extents}; - } - - template - constexpr bool operator==(const static_bounds& rhs) const noexcept - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator!=(const static_bounds& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } - - constexpr const_iterator end() const noexcept - { - return const_iterator(*this, this->index_bounds()); - } -}; - -template -class strided_bounds -{ - template - friend class strided_bounds; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; - - constexpr strided_bounds(const strided_bounds&) noexcept = default; - - constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; - - constexpr strided_bounds(const value_type (&values)[rank], index_type strides) - : m_extents(values), m_strides(std::move(strides)) - { - } - - constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept - : m_extents(extents), - m_strides(strides) - { - } - - constexpr index_type strides() const noexcept { return m_strides; } - - constexpr size_type total_size() const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; ++i) { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - - constexpr size_type size() const noexcept - { - size_type ret = 1; - for (size_t i = 0; i < rank; ++i) { - ret *= m_extents[i]; - } - return ret; - } - - constexpr bool contains(const index_type& idx) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; i++) { - Expects(idx[i] < m_extents[i]); // index is out of bounds of the array - ret += idx[i] * m_strides[i]; - } - return ret; - } - - constexpr size_type stride() const noexcept { return m_strides[0]; } - - template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return {details::shift_left(m_extents), details::shift_left(m_strides)}; - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - - constexpr index_type index_bounds() const noexcept { return m_extents; } - constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } - - constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } - -private: - index_type m_extents; - index_type m_strides; -}; - -template -struct is_bounds : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; - -template -class bounds_iterator : public std::iterator -{ -private: - using Base = std::iterator; - -public: - static const size_t rank = IndexType::rank; - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const noexcept { return curr_; } - - constexpr pointer operator->() const noexcept { return &curr_; } - - constexpr bounds_iterator& operator++() noexcept - { - for (size_t i = rank; i-- > 0;) { - if (curr_[i] < boundary_[i] - 1) { - curr_[i]++; - return *this; - } - curr_[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr_ = boundary_; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - if (!less(curr_, boundary_)) { - // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) { - curr_[i] = boundary_[i] - 1; - } - return *this; - } - for (size_t i = rank; i-- > 0;) { - if (curr_[i] >= 1) { - curr_[i]--; - return *this; - } - curr_[i] = boundary_[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - Expects(false); - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - auto linear_idx = linearize(curr_) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary_[i + 1]; - } - for (size_t i = 0; i < rank; ++i) { - curr_[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - // index is out of bounds of the array - Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return linearize(curr_) - linearize(rhs.curr_); - } - - constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr_ == rhs.curr_; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return less(curr_, rhs.curr_); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - - void swap(bounds_iterator& rhs) noexcept - { - std::swap(boundary_, rhs.boundary_); - std::swap(curr_, rhs.curr_); - } - -private: - constexpr bool less(index_type& one, index_type& other) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (one[i] < other[i]) return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const noexcept - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary_)) { - res = 1; - for (size_t i = rank; i-- > 0;) { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary_[i]; - } - } - else - { - for (size_t i = rank; i-- > 0;) { - res += idx[i] * multiplier; - multiplier *= boundary_[i]; - } - } - return res; - } - - value_type boundary_; - std::remove_const_t curr_; -}; - -template -bounds_iterator operator+(typename bounds_iterator::difference_type n, - const bounds_iterator& rhs) noexcept -{ - return rhs + n; -} - -namespace details -{ - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return {stride}; - } - - template - void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) - { - static_assert(is_bounds::value && is_bounds::value, - "The src type and dest type must be bounds"); - static_assert(std::is_same::value, - "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || - BoundsSrc::static_size == dynamic_range || - BoundsDest::static_size == BoundsSrc::static_size, - "The source bounds must have same size as dest bounds"); - Expects(src.size() == dest.size()); - } - -} // namespace details - -template -class contiguous_span_iterator; -template -class general_span_iterator; - -template -struct dim_t -{ - static const std::ptrdiff_t value = DimSize; -}; -template <> -struct dim_t -{ - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - dim_t(std::ptrdiff_t size) : dvalue(size) {} -}; - -template -constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { - return dim_t(); -} - -template -constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { - return dim_t<>(n); -} - -template -class multi_span; - -template -class strided_span; - -namespace details -{ - template - struct SpanTypeTraits - { - using value_type = T; - using size_type = size_t; - }; - - template - struct SpanTypeTraits::type> - { - using value_type = typename Traits::span_traits::value_type; - using size_type = typename Traits::span_traits::size_type; - }; - - template - struct SpanArrayTraits - { - using type = multi_span; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct SpanArrayTraits : SpanArrayTraits - { - }; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - Expects(BoundsType::static_size <= totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl( - totalSize, std::integral_constant()); - } - - struct Sep - { - }; - - template - T static_as_multi_span_helper(Sep, Args... args) - { - return T{narrow_cast(args)...}; - } - template - std::enable_if_t< - !std::is_same>::value && !std::is_same::value, T> - static_as_multi_span_helper(Arg, Args... args) - { - return static_as_multi_span_helper(args...); - } - template - T static_as_multi_span_helper(dim_t val, Args... args) - { - return static_as_multi_span_helper(args..., val.dvalue); - } - - template - struct static_as_multi_span_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_multi_span_oracle : std::false_type - { - }; - - template - struct is_multi_span_oracle> - : std::true_type - { - }; - - template - struct is_multi_span_oracle> : std::true_type - { - }; - - template - struct is_multi_span : is_multi_span_oracle> - { - }; -} - -template -class multi_span -{ - // TODO do we still need this? - template - friend class multi_span; - -public: - using bounds_type = static_bounds; - static const size_t Rank = bounds_type::rank; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = contiguous_span_iterator; - using const_span = multi_span; - using const_iterator = contiguous_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - -public: - // default constructor - same as constructing from nullptr_t - constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "Default construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr - get an empty multi_span - constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr with size of 0 (helps with template function calls) - template ::value>> - constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - Expects(size == 0); - } - - // construct from a single element - constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) - { - static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || - bounds_type::static_size == 1, - "Construction from a single element only possible " - "for dynamic or fixed spans of length 0 or 1."); - } - - // prevent constructing from temporaries for single-elements - constexpr multi_span(value_type&&) = delete; - - // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) - { - } - - // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), - bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); - } - - // construct from begin,end pointer pair - template ::value && - details::LessThan::value>> - constexpr multi_span(pointer begin, Ptr end) - : multi_span(begin, - details::newBoundsHelper(static_cast(end) - begin)) - { - Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); - } - - // construct from n-dimensions static array - template > - constexpr multi_span(T (&arr)[N]) - : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible::value, - "Cannot construct a multi_span from an array with fewer elements."); - } - - // construct from n-dimensions dynamic array (e.g. new int[m][4]) - // (precedence will be lower than the 1-dimension pointer) - template > - constexpr multi_span(T* const& data, size_type size) - : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - } - - // construct from std::array - template - constexpr multi_span(std::array& arr) - : multi_span(arr.data(), bounds_type{static_bounds{}}) - { - static_assert( - std::is_convertible(*)[]>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // construct from const std::array - template - constexpr multi_span(const std::array, N>& arr) - : multi_span(arr.data(), static_bounds()) - { - static_assert(std::is_convertible>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // prevent constructing from temporary std::array - template - constexpr multi_span(std::array&& arr) = delete; - - // construct from containers - // future: could use contiguous_iterator_traits to identify only contiguous containers - // type-requirements: container must have .size(), operator[] which are value_type compatible - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - constexpr multi_span(Cont& cont) - : multi_span(static_cast(cont.data()), - details::newBoundsHelper(narrow_cast(cont.size()))) - { - } - - // prevent constructing from temporary containers - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - explicit constexpr multi_span(Cont&& cont) = delete; - - // construct from a convertible multi_span - template , - typename = std::enable_if_t::value && - std::is_convertible::value>> - constexpr multi_span(multi_span other) noexcept - : data_(other.data_), - bounds_(other.bounds_) - { - } - -// trivial copy and move -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span(multi_span&&) = default; -#endif - constexpr multi_span(const multi_span&) = default; - -// trivial assignment -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span& operator=(multi_span&&) = default; -#endif - constexpr multi_span& operator=(const multi_span&) = default; - - // first() - extract the first Count elements into a new multi_span - template - constexpr multi_span first() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data(), Count}; - } - - // first() - extract the first count elements into a new multi_span - constexpr multi_span first(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data(), count}; - } - - // last() - extract the last Count elements into a new multi_span - template - constexpr multi_span last() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data() + this->size() - Count, Count}; - } - - // last() - extract the last count elements into a new multi_span - constexpr multi_span last(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data() + this->size() - count, count}; - } - - // subspan() - create a subview of Count elements starting at Offset - template - constexpr multi_span subspan() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(Offset >= 0, "Offset must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - ((Offset <= bounds_type::static_size) && - Count <= bounds_type::static_size - Offset), - "You must describe a sub-range within bounds of the multi_span."); - - Expects(bounds_type::static_size != dynamic_range || - (Offset <= this->size() && Count <= this->size() - Offset)); - return {this->data() + Offset, Count}; - } - - // subspan() - create a subview of count elements starting at offset - // supplying dynamic_range for count will consume all available elements from offset - constexpr multi_span subspan(size_type offset, - size_type count = dynamic_range) const - noexcept - { - Expects((offset >= 0 && offset <= this->size()) && - (count == dynamic_range || (count <= this->size() - offset))); - return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; - } - - // section - creates a non-contiguous, strided multi_span from a contiguous one - constexpr strided_span section(index_type origin, index_type extents) const - noexcept - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - strided_bounds{extents, details::make_stride(bounds())}}; - } - - // length of the multi_span in elements - constexpr size_type size() const noexcept { return bounds_.size(); } - - // length of the multi_span in elements - constexpr size_type length() const noexcept { return this->size(); } - - // length of the multi_span in bytes - constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } - - // length of the multi_span in bytes - constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } - - constexpr bool empty() const noexcept { return this->size() == 0; } - - static constexpr std::size_t rank() { return Rank; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "Dimension should be less than rank (dimension count starts from 0)."); - return bounds_.template extent(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - return bounds_.extent(dim); - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - constexpr pointer data() const noexcept { return data_; } - - template - constexpr reference operator()(FirstIndex index) - { - return this->operator[](narrow_cast(index)); - } - - template - constexpr reference operator()(FirstIndex index, OtherIndices... indices) - { - index_type idx = {narrow_cast(index), - narrow_cast(indices...)}; - return this->operator[](idx); - } - - constexpr reference operator[](const index_type& idx) const noexcept - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const noexcept - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return Ret{data_ + ridx, bounds_.slice()}; - } - - constexpr iterator begin() const noexcept { return iterator{this, true}; } - - constexpr iterator end() const noexcept { return iterator{this, false}; } - - constexpr const_iterator cbegin() const noexcept - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const noexcept - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const noexcept - { - return const_reverse_iterator{cend()}; - } - - constexpr const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator{cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const multi_span& other) const - noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const multi_span& other) const - noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const multi_span& other) const - noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const multi_span& other) const - noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const multi_span& other) const - noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const multi_span& other) const - noexcept - { - return !(*this < other); - } -}; - -// -// Free functions for manipulating spans -// - -// reshape a multi_span into a different dimensionality -// DimCount and Enabled here are workarounds for a bug in MSVC 2015 -template 0), typename = std::enable_if_t> -constexpr auto as_multi_span(SpanType s, Dimensions2... dims) - -> multi_span -{ - static_assert(details::is_multi_span::value, - "Variadic as_multi_span() is for reshaping existing spans."); - using BoundsType = - typename multi_span::bounds_type; - auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(s.bounds(), tobounds); - return {s.data(), tobounds}; -} - -// convert a multi_span to a multi_span -template -multi_span as_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span (a writeable byte multi_span) -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -multi_span as_writeable_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_multi_span(multi_span s) noexcept -> multi_span< - const U, static_cast( - multi_span::bounds_type::static_size != dynamic_range - ? (static_cast( - multi_span::bounds_type::static_size) / - sizeof(U)) - : dynamic_range)> -{ - using ConstByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ConstByteSpan::bounds_type::static_size == dynamic_range || - ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_multi_span(multi_span s) noexcept - -> multi_span( - multi_span::bounds_type::static_size != dynamic_range - ? static_cast( - multi_span::bounds_type::static_size) / - sizeof(U) - : dynamic_range)> -{ - using ByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ByteSpan::bounds_type::static_size == dynamic_range || - ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -template -constexpr auto as_multi_span(T* const& ptr, dim_t... args) - -> multi_span, Dimensions...> -{ - return {reinterpret_cast*>(ptr), - details::static_as_multi_span_helper>(args..., - details::Sep{})}; -} - -template -constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> - typename details::SpanArrayTraits::type -{ - return {reinterpret_cast*>(arr), len}; -} - -template -constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array&&) = delete; - -template -constexpr multi_span as_multi_span(std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(T* begin, T* end) -{ - return {begin, end}; -} - -template -constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> -{ - Expects(arr.size() < PTRDIFF_MAX); - return {arr.data(), narrow_cast(arr.size())}; -} - -template -constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> = delete; - -// from basic_string which doesn't have nonconst .data() member like other contiguous containers -template -constexpr auto as_multi_span(std::basic_string& str) - -> multi_span -{ - Expects(str.size() < PTRDIFF_MAX); - return {&str[0], narrow_cast(str.size())}; -} - -// strided_span is an extension that is not strictly part of the GSL at this time. -// It is kept here while the multidimensional interface is still being defined. -template -class strided_span -{ -public: - using bounds_type = strided_bounds; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = general_span_iterator; - using const_strided_span = strided_span; - using const_iterator = general_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - template - friend class strided_span; - -public: - // from raw data - constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) - : data_(ptr), bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); - // Bounds cross data boundaries - Expects(this->bounds().total_size() <= size); - (void) size; - } - - // from static array of size N - template - constexpr strided_span(value_type (&values)[N], bounds_type bounds) - : strided_span(values, N, std::move(bounds)) - { - } - - // from array view - template ::value, - typename Dummy = std::enable_if_t> - constexpr strided_span(multi_span av, bounds_type bounds) - : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - { - } - - // convertible - template ::value>> - constexpr strided_span(const strided_span& other) - : data_(other.data_), bounds_(other.bounds_) - { - } - - // convert from bytes - template - constexpr strided_span< - typename std::enable_if::value, OtherValueType>::type, - Rank> - as_strided_span() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && - (sizeof(OtherValueType) % sizeof(value_type) == 0), - "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return {const_cast(reinterpret_cast(this->data())), - size, bounds_type{resize_extent(this->bounds().index_bounds(), d), - resize_stride(this->bounds().strides(), d)}}; - } - - constexpr strided_span section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - bounds_type{extents, details::make_stride(bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than Rank (dimension count starts from 0)"); - return bounds_.template extent(); - } - - constexpr size_type size() const noexcept { return bounds_.size(); } - - constexpr pointer data() const noexcept { return data_; } - - constexpr explicit operator bool() const noexcept { return data_ != nullptr; } - - constexpr iterator begin() const { return iterator{this, true}; } - - constexpr iterator end() const { return iterator{this, false}; } - - constexpr const_iterator cbegin() const - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } - - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const strided_span& other) const noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const strided_span& other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const strided_span& other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const strided_span& other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const strided_span& other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const strided_span& other) const noexcept - { - return !(*this < other); - } - -private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - // The last dimension of the array needs to contain a multiple of new type elements - Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); - - index_type ret = extent; - ret[Rank - 1] /= d; - - return ret; - } - - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - - return strides; - } - - template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - // The strides must have contiguous chunks of - // memory that can contain a multiple of new type elements - Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - - for (size_t i = Rank - 1; i > 0; --i) { - // Only strided arrays with regular strides can be resized - Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); - } - - index_type ret = strides / d; - ret[Rank - 1] = 1; - - return ret; - } -}; - -template -class contiguous_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - -private: - template - friend class multi_span; - - pointer data_; - const Span* m_validator; - void validateThis() const - { - // iterator is out of range of the array - Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); - } - contiguous_span_iterator(const Span* container, bool isbegin) - : data_(isbegin ? container->data_ : container->data_ + container->size()) - , m_validator(container) - { - } - -public: - reference operator*() const noexcept - { - validateThis(); - return *data_; - } - pointer operator->() const noexcept - { - validateThis(); - return data_; - } - contiguous_span_iterator& operator++() noexcept - { - ++data_; - return *this; - } - contiguous_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_span_iterator& operator--() noexcept - { - --data_; - return *this; - } - contiguous_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_span_iterator operator+(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret += n; - } - contiguous_span_iterator& operator+=(difference_type n) noexcept - { - data_ += n; - return *this; - } - contiguous_span_iterator operator-(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret -= n; - } - contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ - rhs.data_; - } - reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ == rhs.data_; - } - bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ < rhs.data_; - } - bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(contiguous_span_iterator& rhs) noexcept - { - std::swap(data_, rhs.data_); - std::swap(m_validator, rhs.m_validator); - } -}; - -template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, - const contiguous_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -template -class general_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - -private: - template - friend class strided_span; - - const Span* m_container; - typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span* container, bool isbegin) - : m_container(container) - , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } - -public: - reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() noexcept { return &(*m_container)[*m_itr]; } - general_span_iterator& operator++() noexcept - { - ++m_itr; - return *this; - } - general_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - general_span_iterator& operator--() noexcept - { - --m_itr; - return *this; - } - general_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - general_span_iterator operator+(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret += n; - } - general_span_iterator& operator+=(difference_type n) noexcept - { - m_itr += n; - return *this; - } - general_span_iterator operator-(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret -= n; - } - general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; } - - bool operator==(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(general_span_iterator& rhs) noexcept - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } -}; - -template -general_span_iterator operator+(typename general_span_iterator::difference_type n, - const general_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -} // namespace gsl - -#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_VARIADIC_CTOR_BUG - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#undef noexcept - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pop_macro("noexcept") -#endif - -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - -#endif // GSL_MULTI_SPAN_H diff --git a/include/span.h b/include/span.h deleted file mode 100644 index 1405aa9..0000000 --- a/include/span.h +++ /dev/null @@ -1,826 +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_SPAN_H -#define GSL_SPAN_H - -#include "gsl_assert.h" -#include "gsl_byte.h" -#include "gsl_util.h" -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - -#pragma warning(push) - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(disable : 4127) // conditional expression is constant - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR -#define GSL_MSVC_NO_CPP14_STD_EQUAL - -// noexcept is not understood -#ifndef GSL_THROW_ON_CONTRACT_VIOLATION -#pragma push_macro("noexcept") -#define noexcept /* nothing */ -#endif - -#pragma push_macro("alignof") -#define alignof __alignof - -// turn off some misguided warnings -#pragma warning(push) -#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable : 4512) // warns that assignment op could not be generated - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#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 -{ - -// [views.constants], constants -constexpr const std::ptrdiff_t dynamic_extent = -1; - -template -class span; - -// implementation details -namespace details -{ - template - struct is_span_oracle : std::false_type - { - }; - - template - struct is_span_oracle> : std::true_type - { - }; - - template - struct is_span : public is_span_oracle> - { - }; - - template - struct is_std_array_oracle : std::false_type - { - }; - - template - struct is_std_array_oracle> : std::true_type - { - }; - - template - struct is_std_array : public is_std_array_oracle> - { - }; - - template - struct is_allowed_pointer_conversion - : public std::integral_constant::value && - std::is_pointer::value && - std::is_convertible::value> - { - }; - - template - struct is_allowed_integral_conversion - : public std::integral_constant< - bool, std::is_integral::value && std::is_integral::value && - sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && - std::is_convertible::value> - { - }; - - template - struct is_allowed_extent_conversion - : public std::integral_constant - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value> - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant::value> - { - }; - - template - struct is_allowed_element_type_conversion : public std::true_type - { - }; - - template - class const_span_iterator - { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; - - using const_pointer = std::add_const_t; - using pointer = const_pointer; - - using const_reference = std::add_const_t; - using reference = const_reference; - - constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} - constexpr const_span_iterator(const Span* span, typename Span::index_type index) - : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } - - constexpr reference operator*() const - { - Expects(span_); - return (*span_)[index_]; - } - - constexpr pointer operator->() const - { - Expects(span_); - return &((*span_)[index_]); - } - - constexpr const_span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } - - constexpr const_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr const_span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } - - constexpr const_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr const_span_iterator operator+(difference_type n) const noexcept - { - auto ret = *this; - return ret += n; - } - - constexpr const_span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } - - constexpr const_span_iterator operator-(difference_type n) const noexcept - { - auto ret = *this; - return ret -= n; - } - - constexpr const_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const const_span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } - - constexpr bool operator!=(const const_span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } - - constexpr bool operator<=(const const_span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const const_span_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const const_span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(const_span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(span_, rhs.span_); - } - - private: - const Span* span_; - std::ptrdiff_t index_; - }; - - template - class span_iterator - { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; - - using pointer = value_type*; - using reference = value_type&; - - constexpr span_iterator() : span_iterator(nullptr, 0) {} - constexpr span_iterator(const Span* span, typename Span::index_type index) - : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } - - constexpr reference operator*() const - { - Expects(span_); - return (*span_)[index_]; - } - - constexpr pointer operator->() const - { - Expects(span_); - return &((*span_)[index_]); - } - - constexpr span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } - - constexpr span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } - - constexpr span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr span_iterator operator+(difference_type n) const noexcept - { - auto ret = *this; - return ret += n; - } - - constexpr span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } - - constexpr span_iterator operator-(difference_type n) const noexcept - { - auto ret = *this; - return ret -= n; - } - - constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - - constexpr difference_type operator-(const span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } - - constexpr bool operator!=(const span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } - - constexpr bool operator<=(const span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } - - constexpr bool operator>=(const span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(span_, rhs.span_); - } - - private: - const Span* span_; - std::ptrdiff_t index_; - }; - - template - constexpr const_span_iterator - operator+(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs + n; - } - - template - constexpr const_span_iterator - operator-(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs - n; - } - - template - constexpr span_iterator operator+(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept - { - return rhs + n; - } - - template - constexpr span_iterator operator-(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept - { - return rhs - n; - } - - template - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); - - constexpr extent_type() noexcept {} - - template - constexpr extent_type(extent_type ext) noexcept - { - static_assert(Other == Ext || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); - Expects(ext.size() == Ext); - } - - constexpr extent_type(index_type size) { Expects(size == Ext); } - - constexpr inline index_type size() const noexcept { return Ext; } - }; - - template <> - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - template - explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } - - explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - - constexpr inline index_type size() const noexcept { return size_; } - - private: - index_type size_; - }; -} // namespace details - -// [span], class template span -template -class span -{ -public: - // constants and types - using element_type = ElementType; - using index_type = std::ptrdiff_t; - using pointer = element_type*; - using reference = element_type&; - - using iterator = details::span_iterator>; - using const_iterator = details::const_span_iterator>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - constexpr static const index_type extent = Extent; - - // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} - - constexpr span(std::nullptr_t) noexcept : span() {} - - constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} - - constexpr span(pointer firstElem, pointer lastElem) - : storage_(firstElem, std::distance(firstElem, lastElem)) - { - } - - template - constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) - { - } - - template > - constexpr span(std::array& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - template - constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement - // on Container to be a contiguous sequence container. - template ::value && !details::is_std_array::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(Container& cont) : span(cont.data(), cont.size()) - { - } - - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(const Container& cont) : span(cont.data(), cont.size()) - { - } - - constexpr span(const span& other) noexcept = default; -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr span(span&& other) noexcept = default; -#else - constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {} -#endif - - template < - class OtherElementType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t< - details::is_allowed_extent_conversion::value && - details::is_allowed_element_type_conversion::value>> - constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), - details::extent_type(other.size())) - { - } - - template < - class OtherElementType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t< - details::is_allowed_extent_conversion::value && - details::is_allowed_element_type_conversion::value>> - constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), - details::extent_type(other.size())) - { - } - - ~span() noexcept = default; - constexpr span& operator=(const span& other) noexcept = default; - -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr span& operator=(span&& other) noexcept = default; -#else - constexpr span& operator=(span&& other) noexcept - { - storage_ = std::move(other.storage_); - return *this; - } -#endif - // [span.sub], span subviews - template - constexpr span first() const - { - Expects(Count >= 0 && Count <= size()); - return {data(), Count}; - } - - template - constexpr span last() const - { - Expects(Count >= 0 && Count <= size()); - return {data() + (size() - Count), Count}; - } - - template - constexpr span subspan() const - { - Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && - (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); - return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; - } - - constexpr span first(index_type count) const - { - Expects(count >= 0 && count <= size()); - return {data(), count}; - } - - constexpr span last(index_type count) const - { - Expects(count >= 0 && count <= size()); - return {data() + (size() - count), count}; - } - - constexpr span subspan(index_type offset, - index_type count = dynamic_extent) const - { - Expects((offset == 0 || (offset > 0 && offset <= size())) && - (count == dynamic_extent || (count >= 0 && offset + count <= size()))); - return {data() + offset, count == dynamic_extent ? size() - offset : count}; - } - - // [span.obs], span observers - constexpr index_type length() const noexcept { return size(); } - constexpr index_type size() const noexcept { return storage_.size(); } - constexpr index_type length_bytes() const noexcept { return size_bytes(); } - constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } - constexpr bool empty() const noexcept { return size() == 0; } - - // [span.elem], span element access - constexpr reference operator[](index_type idx) const - { - Expects(idx >= 0 && idx < storage_.size()); - return data()[idx]; - } - constexpr reference operator()(index_type idx) const { return this->operator[](idx); } - constexpr pointer data() const noexcept { return storage_.data(); } - - // [span.iter], span iterator support - iterator begin() const noexcept { return {this, 0}; } - iterator end() const noexcept { return {this, length()}; } - - const_iterator cbegin() const noexcept { return {this, 0}; } - const_iterator cend() const noexcept { return {this, length()}; } - - reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } - const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } - -private: - // this implementation detail class lets us take advantage of the - // empty base class optimization to pay for only storage of a single - // pointer in the case of fixed-size spans - template - class storage_type : public ExtentType - { - public: - template - constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) - { - Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); - } - - constexpr inline pointer data() const noexcept { return data_; } - - private: - pointer data_; - }; - - storage_type> storage_; -}; - -// [span.comparison], span comparison operators -template -constexpr bool operator==(const span& l, - const span& r) -{ -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin()); -#else - return std::equal(l.begin(), l.end(), r.begin(), r.end()); -#endif -} - -template -constexpr bool operator!=(const span& l, const span& r) -{ - return !(l == r); -} - -template -constexpr bool operator<(const span& l, const span& r) -{ - return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); -} - -template -constexpr bool operator<=(const span& l, const span& r) -{ - return !(l > r); -} - -template -constexpr bool operator>(const span& l, const span& r) -{ - return r < l; -} - -template -constexpr bool operator>=(const span& l, const span& r) -{ - return !(l < r); -} - -namespace details -{ - // if we only supported compilers with good constexpr support then - // this pair of classes could collapse down to a constexpr function - - // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as - // constexpr - // and so will fail compilation of the template - template - struct calculate_byte_size - : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> - { - }; - - template - struct calculate_byte_size - : std::integral_constant - { - }; -} - -// [span.objectrep], views of object representation -template -span::value> -as_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template ::value>> -span::value> -as_writeable_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -} // namespace gsl - -#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 - -#pragma pop_macro("alignof") - -#undef GSL_MSVC_HAS_VARIADIC_CTOR_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 - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif // GSL_SPAN_H diff --git a/include/string_span.h b/include/string_span.h deleted file mode 100644 index e18e07a..0000000 --- a/include/string_span.h +++ /dev/null @@ -1,828 +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_SPAN_H -#define GSL_STRING_SPAN_H - -#include "gsl_assert.h" -#include "gsl_util.h" -#include "span.h" -#include -#include -#include - -#ifdef _MSC_VER - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr /* nothing */ - -#pragma warning(push) - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE -#define GSL_MSVC_NO_CPP14_STD_EQUAL -#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR - -// 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 -using basic_zstring = CharT*; - -template -using czstring = basic_zstring; - -template -using cwzstring = basic_zstring; - -template -using zstring = basic_zstring; - -template -using wzstring = basic_zstring; - -// -// 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 -span 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 -inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) -{ - return ensure_sentinel(sz, max); -} - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const -// overloads to share an implementation -inline span ensure_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -template -span ensure_z(T (&sz)[N]) -{ - return ensure_z(&sz[0], static_cast(N)); -} - -template -span::type, dynamic_extent> -ensure_z(Cont& cont) -{ - return ensure_z(cont.data(), static_cast(cont.length())); -} - -template -class basic_string_span; - -namespace details -{ - template - struct is_basic_string_span_oracle : std::false_type - { - }; - - template - struct is_basic_string_span_oracle> : std::true_type - { - }; - - template - struct is_basic_string_span : is_basic_string_span_oracle> - { - }; - - template - struct length_func - { - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(strnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(strnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; -} - -// -// string_span and relatives -// -template -class basic_string_span -{ -public: - using element_type = CharT; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - using impl_type = span; - - using index_type = typename impl_type::index_type; - 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() noexcept = default; - - // copy - constexpr basic_string_span(const basic_string_span& other) noexcept = default; - -// move -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span(basic_string_span&& other) noexcept = default; -#else - constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} -#endif - - // assign - constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; - -// move assign -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; -#else - constexpr basic_string_span& operator=(basic_string_span&& other) noexcept - { - span_ = std::move(other.span_); - return *this; - } -#endif - - // from nullptr - constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {} - - constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} - constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} - - // From static arrays - if 0-terminated, remove 0 from the view - // All other containers allow 0s within the length, so we do not remove them - template - constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) - { - } - - template > - constexpr basic_string_span(std::array& arr) noexcept : span_(arr) - { - } - - template > - constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) - { - } - - // Container signature should work for basic_string after C++17 version exists - template - constexpr basic_string_span(std::basic_string& str) - : span_(&str[0], str.length()) - { - } - - template - constexpr basic_string_span(const std::basic_string& str) - : span_(&str[0], str.length()) - { - } - - // from containers. Containers must have a pointer type and data() function signatures - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(Container& cont) : span_(cont) - { - } - - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(const Container& cont) : span_(cont) - { - } - - // from string_span - template < - class OtherValueType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) - : span_(other.data(), other.length()) - { - } - - template - constexpr basic_string_span first() const - { - return {span_.template first()}; - } - - constexpr basic_string_span first(index_type count) const - { - return {span_.first(count)}; - } - - template - constexpr basic_string_span last() const - { - return {span_.template last()}; - } - - constexpr basic_string_span last(index_type count) const - { - return {span_.last(count)}; - } - - template - constexpr basic_string_span subspan() const - { - return {span_.template subspan()}; - } - - constexpr basic_string_span - subspan(index_type offset, index_type count = dynamic_extent) const - { - return {span_.subspan(offset, count)}; - } - - constexpr reference operator[](index_type idx) const { return span_[idx]; } - constexpr reference operator()(index_type idx) const { return span_[idx]; } - - constexpr pointer data() const { return span_.data(); } - - constexpr index_type length() const noexcept { return span_.size(); } - constexpr index_type size() const noexcept { return span_.size(); } - constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } - constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } - constexpr bool empty() const noexcept { return size() == 0; } - - 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 { return 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) - { - return {sz, details::length_func()(sz, max)}; - } - - template - static impl_type remove_z(element_type (&sz)[N]) - { - return remove_z(&sz[0], narrow_cast(N)); - } - - impl_type span_; -}; - -template -using string_span = basic_string_span; - -template -using cstring_span = basic_string_span; - -template -using wstring_span = basic_string_span; - -template -using cwstring_span = basic_string_span; - -// -// to_string() allow (explicit) conversions from string_span to string -// -#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG - -template -std::basic_string::type> -to_string(basic_string_span view) -{ - return {view.data(), static_cast(view.length())}; -} - -#else - -inline std::string to_string(cstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::string to_string(string_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::wstring to_string(cwstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::wstring to_string(wstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -#endif - -// zero-terminated string span, used to convert -// zero-terminated spans to legacy strings -template -class basic_zstring_span -{ -public: - using value_type = CharT; - using const_value_type = std::add_const_t; - - using pointer = std::add_pointer_t; - using const_pointer = std::add_pointer_t; - - using zstring_type = basic_zstring; - using const_zstring_type = basic_zstring; - - using impl_type = span; - using string_span_type = basic_string_span; - - constexpr basic_zstring_span(impl_type s) noexcept : span_(s) - { - // expects a zero-terminated span - Expects(s[s.size() - 1] == '\0'); - } - - // copy - constexpr basic_zstring_span(const basic_zstring_span& other) = default; - -// move -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_zstring_span(basic_zstring_span&& other) = default; -#else - constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {} -#endif - - // assign - constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; - -// move assign -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; -#else - constexpr basic_zstring_span& operator=(basic_zstring_span&& other) - { - span_ = std::move(other.span_); - return *this; - } -#endif - - constexpr bool empty() const noexcept { return span_.size() == 0; } - - constexpr string_span_type as_string_span() const noexcept - { - return span_.first(span_.size() - 1); - } - - constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } - - constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } - -private: - impl_type span_; -}; - -template -using zstring_span = basic_zstring_span; - -template -using wzstring_span = basic_zstring_span; - -template -using czstring_span = basic_zstring_span; - -template -using cwzstring_span = basic_zstring_span; - -// operator == -template ::value || - std::is_convertible>>::value>> -bool operator==(const gsl::basic_string_span& one, const T& other) noexcept -{ - gsl::basic_string_span> tmp(other); -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); -#else - return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); -#endif -} - -template ::value && - std::is_convertible>>::value>> -bool operator==(const T& one, const gsl::basic_string_span& other) noexcept -{ - gsl::basic_string_span> tmp(one); -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); -#else - return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); -#endif -} - -// operator != -template , Extent>>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one == other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator!=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one == other); -} - -// operator< -template , Extent>>::value>> -bool operator<(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, Extent> tmp(one); - return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); -} -#endif - -// operator <= -template , Extent>>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<=(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(other < one); -} -#endif - -// operator> -template , Extent>>::value>> -bool operator>(gsl::basic_string_span one, const T& other) noexcept -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(gsl::basic_string_span one, const T& other) noexcept -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(const T& one, gsl::basic_string_span other) noexcept -{ - return other < one; -} -#endif - -// operator >= -template , Extent>>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>=(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one < other); -} -#endif -} // namespace GSL - -#ifdef _MSC_VER - -#pragma warning(pop) - -#undef constexpr -#pragma pop_macro("constexpr") - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#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 -#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE -#undef GSL_MSVC_NO_CPP14_STD_EQUAL -#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - -#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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 79931f7..4a59470 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ endif() add_subdirectory(unittest-cpp) include_directories( - ../include + .. ./unittest-cpp ) @@ -33,7 +33,7 @@ else() endif() function(add_gsl_test name) - add_executable(${name} ${name}.cpp ../include/gsl.h ../include/gsl_assert.h ../include/gsl_util.h ../include/multi_span.h ../include/span.h ../include/string_span.h) + add_executable(${name} ${name}.cpp ../gsl/gsl.h ../gsl/gsl_assert.h ../gsl/gsl_util.h ../gsl/multi_span.h ../gsl/span.h ../gsl/string_span.h) target_link_libraries(${name} UnitTest++) install(TARGETS ${name} RUNTIME DESTINATION bin diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp index acd381a..4ac1856 100644 --- a/tests/assertion_tests.cpp +++ b/tests/assertion_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include using namespace gsl; diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp index 1a9f814..53d93d1 100644 --- a/tests/at_tests.cpp +++ b/tests/at_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index 736a85c..7d5a071 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace std; diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 5d4e7f6..183bebf 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index 003d236..b2e5b5f 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index 67b478a..bc12e9b 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index 47c223a..3c82022 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8c9829d..fb20f77 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index b81a5e7..4c07670 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 876886a..fbaa9e8 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include using namespace std; diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 11582de..8c98223 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; -- cgit v1.2.3 From f9f08a56fa08addbd96ab90430735e4e044a3af1 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 9 Aug 2016 18:07:17 -0700 Subject: Added test for Issue #305. --- tests/string_span_tests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 876886a..ca60e4b 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -18,6 +18,7 @@ #include #include #include +#include using namespace std; using namespace gsl; @@ -942,7 +943,13 @@ SUITE(string_span_tests) CHECK(*(str + 3) == L'\0'); } } + } + TEST(Issue305) + { + std::map, int> foo = { { "foo", 0 },{ "bar", 1 } }; + CHECK(foo["foo"] == 0); + CHECK(foo["bar"] == 1); } } -- cgit v1.2.3 From 222c2d85fd2ab37128f5cf00d5267489ea2a8625 Mon Sep 17 00:00:00 2001 From: Galik Date: Wed, 10 Aug 2016 17:24:00 +0100 Subject: Removed .h extension from header files. --- CMakeLists.txt | 8 +- README.md | 7 +- gsl/gsl | 172 ++++ gsl/gsl.h | 172 ---- gsl/gsl_assert | 77 ++ gsl/gsl_assert.h | 77 -- gsl/gsl_byte | 125 +++ gsl/gsl_byte.h | 125 --- gsl/gsl_util | 177 ++++ gsl/gsl_util.h | 177 ---- gsl/multi_span | 2239 ++++++++++++++++++++++++++++++++++++++++++ gsl/multi_span.h | 2239 ------------------------------------------ gsl/span | 826 ++++++++++++++++ gsl/span.h | 826 ---------------- gsl/string_span | 828 ++++++++++++++++ gsl/string_span.h | 828 ---------------- tests/CMakeLists.txt | 2 +- tests/assertion_tests.cpp | 2 +- tests/at_tests.cpp | 2 +- tests/bounds_tests.cpp | 2 +- tests/byte_tests.cpp | 2 +- tests/multi_span_tests.cpp | 2 +- tests/notnull_tests.cpp | 2 +- tests/owner_tests.cpp | 2 +- tests/span_tests.cpp | 2 +- tests/strided_span_tests.cpp | 2 +- tests/string_span_tests.cpp | 2 +- tests/utils_tests.cpp | 2 +- 28 files changed, 4463 insertions(+), 4464 deletions(-) create mode 100644 gsl/gsl delete mode 100644 gsl/gsl.h create mode 100644 gsl/gsl_assert delete mode 100644 gsl/gsl_assert.h create mode 100644 gsl/gsl_byte delete mode 100644 gsl/gsl_byte.h create mode 100644 gsl/gsl_util delete mode 100644 gsl/gsl_util.h create mode 100644 gsl/multi_span delete mode 100644 gsl/multi_span.h create mode 100644 gsl/span delete mode 100644 gsl/span.h create mode 100644 gsl/string_span delete mode 100644 gsl/string_span.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba85857..cb5911c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 2.8.7) project(GSL CXX) set(GSL_HEADERS - "gsl/gsl.h" - "gsl/gsl_assert.h" - "gsl/span.h" - "gsl/string_span.h" + "gsl/gsl" + "gsl/gsl_assert" + "gsl/span" + "gsl/string_span" ) include_directories( diff --git a/README.md b/README.md index f54b0c5..58bb46c 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ The library includes types like `span`, `string_span`, `owner<>` and others. The entire implementation is provided inline in the headers under the [gsl](./gsl) 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. [gsl/span.h](./gsl/span.h)), -it is simplest to just include [gsl/gsl.h](./gsl/gsl.h) and gain access to the entire library. +While some types have been broken out into their own headers (e.g. [gsl/span](./gsl/span)), +it is simplest to just include [gsl/gsl](./gsl/gsl) 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 other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. @@ -78,8 +78,7 @@ MSVC++ GCC/clang -I$HOME/dev/GSL - Include the library using: - #include + #include diff --git a/gsl/gsl b/gsl/gsl new file mode 100644 index 0000000..656ebe0 --- /dev/null +++ b/gsl/gsl @@ -0,0 +1,172 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_GSL_H +#define GSL_GSL_H + +#include "gsl_assert" // Ensures/Expects +#include "gsl_util" // finally()/narrow()/narrow_cast()... +#include "multi_span" // multi_span, strided_span... +#include "span" // span +#include "string_span" // zstring, string_span, zstring_builder... +#include + +#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.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +template +using owner = T; + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from U* fails with nullptr +// - allow implicit conversion to U* +// +template +class not_null +{ + static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); + +public: + not_null(T t) : ptr_(t) { ensure_invariant(); } + not_null& operator=(const T& t) + { + ptr_ = t; + ensure_invariant(); + return *this; + } + + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; + + template ::value>> + not_null(const not_null& other) + { + *this = other; + } + + template ::value>> + not_null& operator=(const not_null& other) + { + ptr_ = other.get(); + return *this; + } + + // prevents compilation when someone attempts to assign a nullptr + not_null(std::nullptr_t) = delete; + not_null(int) = delete; + not_null& operator=(std::nullptr_t) = delete; + not_null& operator=(int) = delete; + + T get() const + { +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } // the assume() should help the optimizer + + operator T() const { return get(); } + T operator->() const { return get(); } + + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } +private: + T ptr_; + + // 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 { Expects(ptr_ != nullptr); } + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+(size_t) = delete; + not_null& operator+=(size_t) = delete; + not_null& operator-(size_t) = delete; + not_null& operator-=(size_t) = delete; +}; + +} // namespace gsl + +namespace std +{ +template +struct hash> +{ + size_t operator()(const gsl::not_null& value) const { return hash{}(value); } +}; + +} // namespace std + +#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_GSL_H diff --git a/gsl/gsl.h b/gsl/gsl.h deleted file mode 100644 index 8e00a44..0000000 --- a/gsl/gsl.h +++ /dev/null @@ -1,172 +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_GSL_H -#define GSL_GSL_H - -#include "gsl_assert.h" // Ensures/Expects -#include "gsl_util.h" // finally()/narrow()/narrow_cast()... -#include "multi_span.h" // multi_span, strided_span... -#include "span.h" // span -#include "string_span.h" // zstring, string_span, zstring_builder... -#include - -#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.owner: ownership pointers -// -using std::unique_ptr; -using std::shared_ptr; - -template -using owner = T; - -// -// not_null -// -// Restricts a pointer or smart pointer to only hold non-null values. -// -// Has zero size overhead over T. -// -// If T is a pointer (i.e. T == U*) then -// - allow construction from U* or U& -// - disallow construction from nullptr_t -// - disallow default construction -// - ensure construction from U* fails with nullptr -// - allow implicit conversion to U* -// -template -class not_null -{ - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); - -public: - not_null(T t) : ptr_(t) { ensure_invariant(); } - not_null& operator=(const T& t) - { - ptr_ = t; - ensure_invariant(); - return *this; - } - - not_null(const not_null& other) = default; - not_null& operator=(const not_null& other) = default; - - template ::value>> - not_null(const not_null& other) - { - *this = other; - } - - template ::value>> - not_null& operator=(const not_null& other) - { - ptr_ = other.get(); - return *this; - } - - // prevents compilation when someone attempts to assign a nullptr - not_null(std::nullptr_t) = delete; - not_null(int) = delete; - not_null& operator=(std::nullptr_t) = delete; - not_null& operator=(int) = delete; - - T get() const - { -#ifdef _MSC_VER - __assume(ptr_ != nullptr); -#endif - return ptr_; - } // the assume() should help the optimizer - - operator T() const { return get(); } - T operator->() const { return get(); } - - bool operator==(const T& rhs) const { return ptr_ == rhs; } - bool operator!=(const T& rhs) const { return !(*this == rhs); } -private: - T ptr_; - - // 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 { Expects(ptr_ != nullptr); } - - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - not_null& operator++() = delete; - not_null& operator--() = delete; - not_null operator++(int) = delete; - not_null operator--(int) = delete; - not_null& operator+(size_t) = delete; - not_null& operator+=(size_t) = delete; - not_null& operator-(size_t) = delete; - not_null& operator-=(size_t) = delete; -}; - -} // namespace gsl - -namespace std -{ -template -struct hash> -{ - size_t operator()(const gsl::not_null& value) const { return hash{}(value); } -}; - -} // namespace std - -#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_GSL_H diff --git a/gsl/gsl_assert b/gsl/gsl_assert new file mode 100644 index 0000000..10de31a --- /dev/null +++ b/gsl/gsl_assert @@ -0,0 +1,77 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +#include + +// +// 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 +// + +namespace gsl +{ +struct fail_fast : public std::runtime_error +{ + explicit fail_fast(char const* const message) : std::runtime_error(message) {} +}; +} + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#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 diff --git a/gsl/gsl_assert.h b/gsl/gsl_assert.h deleted file mode 100644 index 10de31a..0000000 --- a/gsl/gsl_assert.h +++ /dev/null @@ -1,77 +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_CONTRACTS_H -#define GSL_CONTRACTS_H - -#include -#include - -// -// 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 -// - -namespace gsl -{ -struct fail_fast : public std::runtime_error -{ - explicit fail_fast(char const* const message) : std::runtime_error(message) {} -}; -} - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#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 diff --git a/gsl/gsl_byte b/gsl/gsl_byte new file mode 100644 index 0000000..5a9c327 --- /dev/null +++ b/gsl/gsl_byte @@ -0,0 +1,125 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_BYTE_H +#define GSL_BYTE_H + +#ifdef _MSC_VER + +// MSVC 2013 workarounds +#if _MSC_VER <= 1800 + +// constexpr is not understood +#pragma push_macro("constexpr") +#define constexpr + +// noexcept is not understood +#pragma push_macro("noexcept") +#define noexcept + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +namespace gsl +{ +// This is a simple definition for now that allows +// use of byte within span<> to be standards-compliant +enum class byte : unsigned char +{ +}; + +template ::value>> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte operator<<(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) << shift); +} + +template ::value>> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) >> shift); +} + +template ::value>> +constexpr byte operator>>(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) >> shift); +} + +constexpr byte& operator|=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) | static_cast(r)); +} + +constexpr byte operator|(byte l, byte r) noexcept +{ + return byte(static_cast(l) + static_cast(r)); +} + +constexpr byte& operator&=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) & static_cast(r)); +} + +constexpr byte operator&(byte l, byte r) noexcept +{ + return byte(static_cast(l) & static_cast(r)); +} + +constexpr byte& operator^=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator^(byte l, byte r) noexcept +{ + return byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } + +template ::value>> +constexpr IntegerType to_integer(byte b) noexcept +{ + return {b}; +} + +} // namespace gsl + +#ifdef _MSC_VER + +#if _MSC_VER <= 1800 + +#undef constexpr +#pragma pop_macro("constexpr") + +#undef noexcept +#pragma pop_macro("noexcept") + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#endif // GSL_BYTE_H \ No newline at end of file diff --git a/gsl/gsl_byte.h b/gsl/gsl_byte.h deleted file mode 100644 index 5a9c327..0000000 --- a/gsl/gsl_byte.h +++ /dev/null @@ -1,125 +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_BYTE_H -#define GSL_BYTE_H - -#ifdef _MSC_VER - -// MSVC 2013 workarounds -#if _MSC_VER <= 1800 - -// constexpr is not understood -#pragma push_macro("constexpr") -#define constexpr - -// noexcept is not understood -#pragma push_macro("noexcept") -#define noexcept - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -namespace gsl -{ -// This is a simple definition for now that allows -// use of byte within span<> to be standards-compliant -enum class byte : unsigned char -{ -}; - -template ::value>> -constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte operator<<(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) << shift); -} - -template ::value>> -constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept -{ - return b = byte(static_cast(b) >> shift); -} - -template ::value>> -constexpr byte operator>>(byte b, IntegerType shift) noexcept -{ - return byte(static_cast(b) >> shift); -} - -constexpr byte& operator|=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) | static_cast(r)); -} - -constexpr byte operator|(byte l, byte r) noexcept -{ - return byte(static_cast(l) + static_cast(r)); -} - -constexpr byte& operator&=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) & static_cast(r)); -} - -constexpr byte operator&(byte l, byte r) noexcept -{ - return byte(static_cast(l) & static_cast(r)); -} - -constexpr byte& operator^=(byte& l, byte r) noexcept -{ - return l = byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator^(byte l, byte r) noexcept -{ - return byte(static_cast(l) ^ static_cast(r)); -} - -constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } - -template ::value>> -constexpr IntegerType to_integer(byte b) noexcept -{ - return {b}; -} - -} // namespace gsl - -#ifdef _MSC_VER - -#if _MSC_VER <= 1800 - -#undef constexpr -#pragma pop_macro("constexpr") - -#undef noexcept -#pragma pop_macro("noexcept") - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#endif // GSL_BYTE_H \ No newline at end of file diff --git a/gsl/gsl_util b/gsl/gsl_util new file mode 100644 index 0000000..4a0dabe --- /dev/null +++ b/gsl/gsl_util @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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" // Ensures/Expects +#include +#include +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// 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 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 +inline final_act finally(const F& f) noexcept +{ + return final_act(f); +} + +template +inline final_act finally(F&& f) noexcept +{ + return final_act(std::forward(f)); +} + +// narrow_cast(): a searchable way to do narrowing casts of values +template +inline constexpr T narrow_cast(U u) noexcept +{ + return static_cast(u); +} + +struct narrowing_error : public std::exception +{ +}; + +namespace details +{ + template + struct is_same_signedness + : public std::integral_constant::value == std::is_signed::value> + { + }; +} + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +inline T narrow(U u) +{ + T t = narrow_cast(u); + if (static_cast(t) != u) throw narrowing_error(); + if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) + throw narrowing_error(); + return t; +} + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +constexpr T& at(T (&arr)[N], size_t index) +{ + Expects(index < N); + return arr[index]; +} + +template +constexpr T& at(std::array& arr, size_t index) +{ + Expects(index < N); + return arr[index]; +} + +template +constexpr typename Cont::value_type& at(Cont& cont, size_t index) +{ + Expects(index < cont.size()); + return cont[index]; +} + +template +constexpr const T& at(std::initializer_list cont, size_t index) +{ + Expects(index < cont.size()); + return *(cont.begin() + index); +} + +} // namespace gsl + +#ifdef _MSC_VER + +#pragma warning(pop) + +#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 diff --git a/gsl/gsl_util.h b/gsl/gsl_util.h deleted file mode 100644 index 92a795b..0000000 --- a/gsl/gsl_util.h +++ /dev/null @@ -1,177 +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_UTIL_H -#define GSL_UTIL_H - -#include "gsl_assert.h" // Ensures/Expects -#include -#include -#include -#include - -#ifdef _MSC_VER - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -// 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 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 -inline final_act finally(const F& f) noexcept -{ - return final_act(f); -} - -template -inline final_act finally(F&& f) noexcept -{ - return final_act(std::forward(f)); -} - -// narrow_cast(): a searchable way to do narrowing casts of values -template -inline constexpr T narrow_cast(U u) noexcept -{ - return static_cast(u); -} - -struct narrowing_error : public std::exception -{ -}; - -namespace details -{ - template - struct is_same_signedness - : public std::integral_constant::value == std::is_signed::value> - { - }; -} - -// narrow() : a checked version of narrow_cast() that throws if the cast changed the value -template -inline T narrow(U u) -{ - T t = narrow_cast(u); - if (static_cast(t) != u) throw narrowing_error(); - if (!details::is_same_signedness::value && ((t < T{}) != (u < U{}))) - throw narrowing_error(); - return t; -} - -// -// at() - Bounds-checked way of accessing static arrays, std::array, std::vector -// -template -constexpr T& at(T (&arr)[N], size_t index) -{ - Expects(index < N); - return arr[index]; -} - -template -constexpr T& at(std::array& arr, size_t index) -{ - Expects(index < N); - return arr[index]; -} - -template -constexpr typename Cont::value_type& at(Cont& cont, size_t index) -{ - Expects(index < cont.size()); - return cont[index]; -} - -template -constexpr const T& at(std::initializer_list cont, size_t index) -{ - Expects(index < cont.size()); - return *(cont.begin() + index); -} - -} // namespace gsl - -#ifdef _MSC_VER - -#pragma warning(pop) - -#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 diff --git a/gsl/multi_span b/gsl/multi_span new file mode 100644 index 0000000..fcc53b8 --- /dev/null +++ b/gsl/multi_span @@ -0,0 +1,2239 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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_MULTI_SPAN_H +#define GSL_MULTI_SPAN_H + +#include "gsl_assert" +#include "gsl_byte" +#include "gsl_util" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const SizeType max_value = std::numeric_limits::max(); + }; + + template + class are_integral : public std::integral_constant + { + }; + + template + class are_integral + : public std::integral_constant::value && are_integral::value> + { + }; +} + +template +class index final +{ + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class index; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using size_type = value_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + + constexpr index() noexcept {} + + constexpr index(const value_type (&values)[Rank]) noexcept + { + std::copy(values, values + Rank, elems); + } + +#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG + template < + typename T, typename... Ts, + typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && + details::are_integral::value>> + constexpr index(T t, Ts... ds) + : index({narrow_cast(t), narrow_cast(ds)...}) + { + } +#else + template ::value>> + constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + { + } +#endif + + constexpr index(const index& other) noexcept = default; + + constexpr index& operator=(const index& rhs) noexcept = default; + + // Preconditions: component_idx < rank + constexpr reference operator[](size_t component_idx) + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + // Preconditions: component_idx < rank + constexpr const_reference operator[](size_t component_idx) const noexcept + { + Expects(component_idx < Rank); // Component index must be less than rank + return elems[component_idx]; + } + + constexpr bool operator==(const index& rhs) const noexcept + { + return std::equal(elems, elems + rank, rhs.elems); + } + + constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } + + constexpr index operator+() const noexcept { return *this; } + + constexpr index operator-() const noexcept + { + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; + } + + constexpr index operator+(const index& rhs) const noexcept + { + index ret = *this; + ret += rhs; + return ret; + } + + constexpr index operator-(const index& rhs) const noexcept + { + index ret = *this; + ret -= rhs; + return ret; + } + + constexpr index& operator+=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + return *this; + } + + constexpr index& operator-=(const index& rhs) noexcept + { + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); + return *this; + } + + constexpr index operator*(value_type v) const noexcept + { + index ret = *this; + ret *= v; + return ret; + } + + constexpr index operator/(value_type v) const noexcept + { + index ret = *this; + ret /= v; + return ret; + } + + friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, + [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + +private: + value_type elems[Rank] = {}; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return narrow_cast(-1); + } + + template ::value>> + constexpr bool operator==(T other) const noexcept + { + return narrow_cast(-1) == other; + } + + template ::value>> + constexpr bool operator!=(T other) const noexcept + { + return narrow_cast(-1) != other; + } +}; + +template ::value>> +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const std::ptrdiff_t dynamic_range = -1; +#endif + +struct generalized_mapping_tag +{ +}; +struct contiguous_mapping_tag : generalized_mapping_tag +{ +}; + +namespace details +{ + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges + { + using size_type = std::ptrdiff_t; + static const size_type Depth = 0; + static const size_type DynamicNum = 0; + static const size_type CurrentRange = 1; + static const size_type TotalSize = 1; + + // TODO : following signature is for work around VS bug + template + BoundsRanges(const OtherRange&, bool /* firstLevel */) + { + } + + BoundsRanges(const BoundsRanges&) = default; + BoundsRanges& operator=(const BoundsRanges&) = default; + BoundsRanges(const std::ptrdiff_t* const) {} + BoundsRanges() = default; + + template + void serialize(T&) const + { + } + + template + size_type linearize(const T&) const + { + return 0; + } + + template + size_type contains(const T&) const + { + return -1; + } + + size_type elementNum(size_t) const noexcept { return 0; } + + size_type totalSize() const noexcept { return TotalSize; } + + bool operator==(const BoundsRanges&) const noexcept { return true; } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum + 1; + static const size_type CurrentRange = dynamic_range; + static const size_type TotalSize = dynamic_range; + const size_type m_bound; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) + : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) + { + Expects(0 <= *arr); + } + + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges& other, + bool /* firstLevel */ = true) + : Base(static_cast&>(other), false) + , m_bound(other.totalSize()) + { + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + const size_type index = this->Base::totalSize() * arr[Dim]; + Expects(index < m_bound); + return index + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return cur < m_bound ? cur + last : -1; + } + + size_type totalSize() const noexcept { return m_bound; } + + size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return m_bound == rhs.m_bound && + static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges + { + using Base = BoundsRanges; + using size_type = std::ptrdiff_t; + static const size_t Depth = Base::Depth + 1; + static const size_t DynamicNum = Base::DynamicNum; + static const size_type CurrentRange = CurRange; + static const size_type TotalSize = + Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; + + BoundsRanges(const BoundsRanges&) = default; + + BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges& other, + bool firstLevel = true) + : Base(static_cast&>(other), false) + { + (void) firstLevel; + } + + template + void serialize(T& arr) const + { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + size_type linearize(const T& arr) const + { + Expects(arr[Dim] < CurrentRange); // Index is out of range + return this->Base::totalSize() * arr[Dim] + + this->Base::template linearize(arr); + } + + template + size_type contains(const T& arr) const + { + if (arr[Dim] >= CurrentRange) return -1; + const size_type last = this->Base::template contains(arr); + if (last == -1) return -1; + return this->Base::totalSize() * arr[Dim] + last; + } + + size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } + + size_type elementNum() const noexcept { return CurrentRange; } + + size_type elementNum(size_t dim) const noexcept + { + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator==(const BoundsRanges& rhs) const noexcept + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible + : public std::integral_constant= TargetType::TotalSize || + TargetType::TotalSize == dynamic_range || + SourceType::TotalSize == dynamic_range || + TargetType::TotalSize == 0)> + { + }; + + template + struct TypeListIndexer + { + const TypeChain& obj_; + TypeListIndexer(const TypeChain& obj) : obj_(obj) {} + + template + const TypeChain& getObj(std::true_type) + { + return obj_; + } + + template + auto getObj(std::false_type) + -> decltype(TypeListIndexer(static_cast(obj_)).template get()) + { + return TypeListIndexer(static_cast(obj_)).template get(); + } + + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain& obj) + { + return TypeListIndexer(obj); + } + + template 1), + typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret{}; + for (size_t i = 0; i < Rank - 1; ++i) { + ret[i] = other[i + 1]; + } + return ret; + } +} + +template +class bounds_iterator; + +template +class static_bounds +{ +public: + static_bounds(const details::BoundsRanges&) {} +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges; + + MyRanges m_ranges; + constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + + template + friend class static_bounds; + +public: + static const size_t rank = MyRanges::Depth; + static const size_t dynamic_rank = MyRanges::DynamicNum; + static const std::ptrdiff_t static_size = MyRanges::TotalSize; + + using size_type = std::ptrdiff_t; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = std::ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; + + constexpr static_bounds(const static_bounds&) = default; + + template + struct BoundsRangeConvertible2; + + template > + static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant())) + { + }; + + template + struct BoundsRangeConvertible2 : std::true_type + { + }; + + template + struct BoundsRangeConvertible + : decltype(helpBoundsRangeConvertible( + SourceType(), TargetType(), + std::integral_constant::value || + TargetType::CurrentRange == dynamic_range || + SourceType::CurrentRange == dynamic_range)>())) + { + }; + + template + struct BoundsRangeConvertible : std::true_type + { + }; + + template , + details::BoundsRanges>::value>> + constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) + { + Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || + MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); + } + + constexpr static_bounds(std::initializer_list il) + : m_ranges(static_cast(il.begin())) + { + // Size of the initializer list must match the rank of the array + Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || + MyRanges::DynamicNum == il.size()); + // Size of the range must be less than the max element of the size type + Expects(m_ranges.totalSize() <= PTRDIFF_MAX); + } + + constexpr static_bounds() = default; + + constexpr static_bounds& operator=(const static_bounds& otherBounds) + { + new (&m_ranges) MyRanges(otherBounds.m_ranges); + return *this; + } + + constexpr sliced_type slice() const noexcept + { + return sliced_type{static_cast&>(m_ranges)}; + } + + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } + + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } + + constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } + + constexpr bool contains(const index_type& idx) const noexcept + { + return m_ranges.contains(idx) != -1; + } + + constexpr size_type operator[](size_t index) const noexcept + { + return m_ranges.elementNum(index); + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < rank, + "dimension should be less than rank (dimension count starts from 0)"); + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + static_assert(std::is_integral::value, + "Dimension parameter must be supplied as an integral type."); + auto real_dim = narrow_cast(dim); + Expects(real_dim < rank); + + return m_ranges.elementNum(real_dim); + } + + constexpr index_type index_bounds() const noexcept + { + size_type extents[rank] = {}; + m_ranges.serialize(extents); + return {extents}; + } + + template + constexpr bool operator==(const static_bounds& rhs) const noexcept + { + return this->size() == rhs.size(); + } + + template + constexpr bool operator!=(const static_bounds& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } + + constexpr const_iterator end() const noexcept + { + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds +{ + template + friend class strided_bounds; + +public: + static const size_t rank = Rank; + using value_type = std::ptrdiff_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using size_type = value_type; + using difference_type = value_type; + using index_type = index; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const value_type dynamic_rank = rank; + static const value_type static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + + constexpr strided_bounds(const strided_bounds&) noexcept = default; + + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; + + constexpr strided_bounds(const value_type (&values)[rank], index_type strides) + : m_extents(values), m_strides(std::move(strides)) + { + } + + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), + m_strides(strides) + { + } + + constexpr index_type strides() const noexcept { return m_strides; } + + constexpr size_type total_size() const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; ++i) { + ret += (m_extents[i] - 1) * m_strides[i]; + } + return ret + 1; + } + + constexpr size_type size() const noexcept + { + size_type ret = 1; + for (size_t i = 0; i < rank; ++i) { + ret *= m_extents[i]; + } + return ret; + } + + constexpr bool contains(const index_type& idx) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; + } + return true; + } + + constexpr size_type linearize(const index_type& idx) const noexcept + { + size_type ret = 0; + for (size_t i = 0; i < rank; i++) { + Expects(idx[i] < m_extents[i]); // index is out of bounds of the array + ret += idx[i] * m_strides[i]; + } + return ret; + } + + constexpr size_type stride() const noexcept { return m_strides[0]; } + + template 1), typename Ret = std::enable_if_t> + constexpr sliced_type slice() const + { + return {details::shift_left(m_extents), details::shift_left(m_strides)}; + } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than rank (dimension count starts from 0)"); + return m_extents[Dim]; + } + + constexpr index_type index_bounds() const noexcept { return m_extents; } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } + +private: + index_type m_extents; + index_type m_strides; +}; + +template +struct is_bounds : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; +template +struct is_bounds> : std::integral_constant +{ +}; + +template +class bounds_iterator : public std::iterator +{ +private: + using Base = std::iterator; + +public: + static const size_t rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::value_type; + template + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary_(bnd.index_bounds()), + curr_(std::move(curr)) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + + constexpr reference operator*() const noexcept { return curr_; } + + constexpr pointer operator->() const noexcept { return &curr_; } + + constexpr bounds_iterator& operator++() noexcept + { + for (size_t i = rank; i-- > 0;) { + if (curr_[i] < boundary_[i] - 1) { + curr_[i]++; + return *this; + } + curr_[i] = 0; + } + // If we're here we've wrapped over - set to past-the-end. + curr_ = boundary_; + return *this; + } + + constexpr bounds_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr bounds_iterator& operator--() noexcept + { + if (!less(curr_, boundary_)) { + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { + curr_[i] = boundary_[i] - 1; + } + return *this; + } + for (size_t i = rank; i-- > 0;) { + if (curr_[i] >= 1) { + curr_[i]--; + return *this; + } + curr_[i] = boundary_[i] - 1; + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + Expects(false); + return *this; + } + + constexpr bounds_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr bounds_iterator operator+(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret += n; + } + + constexpr bounds_iterator& operator+=(difference_type n) noexcept + { + auto linear_idx = linearize(curr_) + n; + std::remove_const_t stride = 0; + stride[rank - 1] = 1; + for (size_t i = rank - 1; i-- > 0;) { + stride[i] = stride[i + 1] * boundary_[i + 1]; + } + for (size_t i = 0; i < rank; ++i) { + curr_[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + // index is out of bounds of the array + Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); + return *this; + } + + constexpr bounds_iterator operator-(difference_type n) const noexcept + { + bounds_iterator ret{*this}; + return ret -= n; + } + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept + { + return linearize(curr_) - linearize(rhs.curr_); + } + + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept + { + return curr_ == rhs.curr_; + } + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept + { + return less(curr_, rhs.curr_); + } + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + + void swap(bounds_iterator& rhs) noexcept + { + std::swap(boundary_, rhs.boundary_); + std::swap(curr_, rhs.curr_); + } + +private: + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) { + if (one[i] < other[i]) return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept + { + // TODO: Smarter impl. + // Check if past-the-end + index_size_type multiplier = 1; + index_size_type res = 0; + if (!less(idx, boundary_)) { + res = 1; + for (size_t i = rank; i-- > 0;) { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary_[i]; + } + } + else + { + for (size_t i = rank; i-- > 0;) { + res += idx[i] * multiplier; + multiplier *= boundary_[i]; + } + } + return res; + } + + value_type boundary_; + std::remove_const_t curr_; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, + const bounds_iterator& rhs) noexcept +{ + return rhs + n; +} + +namespace details +{ + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + return bnd.strides(); + } + + // Make a stride vector from bounds, assuming contiguous memory. + template + constexpr std::enable_if_t< + std::is_same::value, + typename Bounds::index_type> + make_stride(const Bounds& bnd) noexcept + { + auto extents = bnd.index_bounds(); + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return {stride}; + } + + template + void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) + { + static_assert(is_bounds::value && is_bounds::value, + "The src type and dest type must be bounds"); + static_assert(std::is_same::value, + "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || + BoundsSrc::static_size == dynamic_range || + BoundsDest::static_size == BoundsSrc::static_size, + "The source bounds must have same size as dest bounds"); + Expects(src.size() == dest.size()); + } + +} // namespace details + +template +class contiguous_span_iterator; +template +class general_span_iterator; + +template +struct dim_t +{ + static const std::ptrdiff_t value = DimSize; +}; +template <> +struct dim_t +{ + static const std::ptrdiff_t value = dynamic_range; + const std::ptrdiff_t dvalue; + dim_t(std::ptrdiff_t size) : dvalue(size) {} +}; + +template +constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { + return dim_t(); +} + +template +constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { + return dim_t<>(n); +} + +template +class multi_span; + +template +class strided_span; + +namespace details +{ + template + struct SpanTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct SpanTypeTraits::type> + { + using value_type = typename Traits::span_traits::value_type; + using size_type = typename Traits::span_traits::size_type; + }; + + template + struct SpanArrayTraits + { + using type = multi_span; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct SpanArrayTraits : SpanArrayTraits + { + }; + + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size + { + Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); + return BoundsType{totalSize}; + } + template + BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size + { + Expects(BoundsType::static_size <= totalSize); + return {}; + } + template + BoundsType newBoundsHelper(std::ptrdiff_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl( + totalSize, std::integral_constant()); + } + + struct Sep + { + }; + + template + T static_as_multi_span_helper(Sep, Args... args) + { + return T{narrow_cast(args)...}; + } + template + std::enable_if_t< + !std::is_same>::value && !std::is_same::value, T> + static_as_multi_span_helper(Arg, Args... args) + { + return static_as_multi_span_helper(args...); + } + template + T static_as_multi_span_helper(dim_t val, Args... args) + { + return static_as_multi_span_helper(args..., val.dvalue); + } + + template + struct static_as_multi_span_static_bounds_helper + { + using type = static_bounds<(Dimensions::value)...>; + }; + + template + struct is_multi_span_oracle : std::false_type + { + }; + + template + struct is_multi_span_oracle> + : std::true_type + { + }; + + template + struct is_multi_span_oracle> : std::true_type + { + }; + + template + struct is_multi_span : is_multi_span_oracle> + { + }; +} + +template +class multi_span +{ + // TODO do we still need this? + template + friend class multi_span; + +public: + using bounds_type = static_bounds; + static const size_t Rank = bounds_type::rank; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = contiguous_span_iterator; + using const_span = multi_span; + using const_iterator = contiguous_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + +public: + // default constructor - same as constructing from nullptr_t + constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "Default construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr - get an empty multi_span + constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + } + + // construct from nullptr with size of 0 (helps with template function calls) + template ::value>> + constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) + { + static_assert(bounds_type::dynamic_rank != 0 || + (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), + "nullptr_t construction of multi_span only possible " + "for dynamic or fixed, zero-length spans."); + Expects(size == 0); + } + + // construct from a single element + constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) + { + static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || + bounds_type::static_size == 1, + "Construction from a single element only possible " + "for dynamic or fixed spans of length 0 or 1."); + } + + // prevent constructing from temporaries for single-elements + constexpr multi_span(value_type&&) = delete; + + // construct from pointer + length + constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) + { + } + + // construct from pointer + length - multidimensional + constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), + bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); + } + + // construct from begin,end pointer pair + template ::value && + details::LessThan::value>> + constexpr multi_span(pointer begin, Ptr end) + : multi_span(begin, + details::newBoundsHelper(static_cast(end) - begin)) + { + Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); + } + + // construct from n-dimensions static array + template > + constexpr multi_span(T (&arr)[N]) + : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible::value, + "Cannot construct a multi_span from an array with fewer elements."); + } + + // construct from n-dimensions dynamic array (e.g. new int[m][4]) + // (precedence will be lower than the 1-dimension pointer) + template > + constexpr multi_span(T* const& data, size_type size) + : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) + { + static_assert(std::is_convertible::value, + "Cannot convert from source type to target multi_span type."); + } + + // construct from std::array + template + constexpr multi_span(std::array& arr) + : multi_span(arr.data(), bounds_type{static_bounds{}}) + { + static_assert( + std::is_convertible(*)[]>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // construct from const std::array + template + constexpr multi_span(const std::array, N>& arr) + : multi_span(arr.data(), static_bounds()) + { + static_assert(std::is_convertible>::value, + "Cannot convert from source type to target multi_span type."); + static_assert(std::is_convertible, bounds_type>::value, + "You cannot construct a multi_span from a std::array of smaller size."); + } + + // prevent constructing from temporary std::array + template + constexpr multi_span(std::array&& arr) = delete; + + // construct from containers + // future: could use contiguous_iterator_traits to identify only contiguous containers + // type-requirements: container must have .size(), operator[] which are value_type compatible + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + constexpr multi_span(Cont& cont) + : multi_span(static_cast(cont.data()), + details::newBoundsHelper(narrow_cast(cont.size()))) + { + } + + // prevent constructing from temporary containers + template ::value && + std::is_convertible::value && + std::is_same().size(), + *std::declval().data())>, + DataType>::value>> + explicit constexpr multi_span(Cont&& cont) = delete; + + // construct from a convertible multi_span + template , + typename = std::enable_if_t::value && + std::is_convertible::value>> + constexpr multi_span(multi_span other) noexcept + : data_(other.data_), + bounds_(other.bounds_) + { + } + +// trivial copy and move +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span(multi_span&&) = default; +#endif + constexpr multi_span(const multi_span&) = default; + +// trivial assignment +#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT + constexpr multi_span& operator=(multi_span&&) = default; +#endif + constexpr multi_span& operator=(const multi_span&) = default; + + // first() - extract the first Count elements into a new multi_span + template + constexpr multi_span first() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data(), Count}; + } + + // first() - extract the first count elements into a new multi_span + constexpr multi_span first(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data(), count}; + } + + // last() - extract the last Count elements into a new multi_span + template + constexpr multi_span last() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + Count <= bounds_type::static_size, + "Count is out of bounds."); + + Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); + return {this->data() + this->size() - Count, Count}; + } + + // last() - extract the last count elements into a new multi_span + constexpr multi_span last(size_type count) const noexcept + { + Expects(count >= 0 && count <= this->size()); + return {this->data() + this->size() - count, count}; + } + + // subspan() - create a subview of Count elements starting at Offset + template + constexpr multi_span subspan() const noexcept + { + static_assert(Count >= 0, "Count must be >= 0."); + static_assert(Offset >= 0, "Offset must be >= 0."); + static_assert(bounds_type::static_size == dynamic_range || + ((Offset <= bounds_type::static_size) && + Count <= bounds_type::static_size - Offset), + "You must describe a sub-range within bounds of the multi_span."); + + Expects(bounds_type::static_size != dynamic_range || + (Offset <= this->size() && Count <= this->size() - Offset)); + return {this->data() + Offset, Count}; + } + + // subspan() - create a subview of count elements starting at offset + // supplying dynamic_range for count will consume all available elements from offset + constexpr multi_span subspan(size_type offset, + size_type count = dynamic_range) const + noexcept + { + Expects((offset >= 0 && offset <= this->size()) && + (count == dynamic_range || (count <= this->size() - offset))); + return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; + } + + // section - creates a non-contiguous, strided multi_span from a contiguous one + constexpr strided_span section(index_type origin, index_type extents) const + noexcept + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + strided_bounds{extents, details::make_stride(bounds())}}; + } + + // length of the multi_span in elements + constexpr size_type size() const noexcept { return bounds_.size(); } + + // length of the multi_span in elements + constexpr size_type length() const noexcept { return this->size(); } + + // length of the multi_span in bytes + constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } + + // length of the multi_span in bytes + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } + + constexpr bool empty() const noexcept { return this->size() == 0; } + + static constexpr std::size_t rank() { return Rank; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "Dimension should be less than rank (dimension count starts from 0)."); + return bounds_.template extent(); + } + + template + constexpr size_type extent(IntType dim) const noexcept + { + return bounds_.extent(dim); + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + constexpr pointer data() const noexcept { return data_; } + + template + constexpr reference operator()(FirstIndex index) + { + return this->operator[](narrow_cast(index)); + } + + template + constexpr reference operator()(FirstIndex index, OtherIndices... indices) + { + index_type idx = {narrow_cast(index), + narrow_cast(indices...)}; + return this->operator[](idx); + } + + constexpr reference operator[](const index_type& idx) const noexcept + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const noexcept + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return Ret{data_ + ridx, bounds_.slice()}; + } + + constexpr iterator begin() const noexcept { return iterator{this, true}; } + + constexpr iterator end() const noexcept { return iterator{this, false}; } + + constexpr const_iterator cbegin() const noexcept + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const noexcept + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator{cend()}; + } + + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator{cbegin()}; + } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const multi_span& other) const + noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const multi_span& other) const + noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const multi_span& other) const + noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const multi_span& other) const + noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const multi_span& other) const + noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const multi_span& other) const + noexcept + { + return !(*this < other); + } +}; + +// +// Free functions for manipulating spans +// + +// reshape a multi_span into a different dimensionality +// DimCount and Enabled here are workarounds for a bug in MSVC 2015 +template 0), typename = std::enable_if_t> +constexpr auto as_multi_span(SpanType s, Dimensions2... dims) + -> multi_span +{ + static_assert(details::is_multi_span::value, + "Variadic as_multi_span() is for reshaping existing spans."); + using BoundsType = + typename multi_span::bounds_type; + auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(s.bounds(), tobounds); + return {s.data(), tobounds}; +} + +// convert a multi_span to a multi_span +template +multi_span as_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span (a writeable byte multi_span) +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +multi_span as_writeable_bytes(multi_span s) noexcept +{ + static_assert(std::is_trivial>::value, + "The value_type of multi_span must be a trivial type."); + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_multi_span(multi_span s) noexcept -> multi_span< + const U, static_cast( + multi_span::bounds_type::static_size != dynamic_range + ? (static_cast( + multi_span::bounds_type::static_size) / + sizeof(U)) + : dynamic_range)> +{ + using ConstByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ConstByteSpan::bounds_type::static_size == dynamic_range || + ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +// convert a multi_span to a multi_span +// this is not currently a portable function that can be relied upon to work +// on all implementations. It should be considered an experimental extension +// to the standard GSL interface. +template +constexpr auto as_multi_span(multi_span s) noexcept + -> multi_span( + multi_span::bounds_type::static_size != dynamic_range + ? static_cast( + multi_span::bounds_type::static_size) / + sizeof(U) + : dynamic_range)> +{ + using ByteSpan = multi_span; + static_assert( + std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), + "Target type must be a trivial type and its size must match the byte array size"); + + Expects((s.size_bytes() % sizeof(U)) == 0); + return {reinterpret_cast(s.data()), + s.size_bytes() / narrow_cast(sizeof(U))}; +} + +template +constexpr auto as_multi_span(T* const& ptr, dim_t... args) + -> multi_span, Dimensions...> +{ + return {reinterpret_cast*>(ptr), + details::static_as_multi_span_helper>(args..., + details::Sep{})}; +} + +template +constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> + typename details::SpanArrayTraits::type +{ + return {reinterpret_cast*>(arr), len}; +} + +template +constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(const std::array&&) = delete; + +template +constexpr multi_span as_multi_span(std::array& arr) +{ + return {arr}; +} + +template +constexpr multi_span as_multi_span(T* begin, T* end) +{ + return {begin, end}; +} + +template +constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> +{ + Expects(arr.size() < PTRDIFF_MAX); + return {arr.data(), narrow_cast(arr.size())}; +} + +template +constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< + !details::is_multi_span>::value, + multi_span, dynamic_range>> = delete; + +// from basic_string which doesn't have nonconst .data() member like other contiguous containers +template +constexpr auto as_multi_span(std::basic_string& str) + -> multi_span +{ + Expects(str.size() < PTRDIFF_MAX); + return {&str[0], narrow_cast(str.size())}; +} + +// strided_span is an extension that is not strictly part of the GSL at this time. +// It is kept here while the multidimensional interface is still being defined. +template +class strided_span +{ +public: + using bounds_type = strided_bounds; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using const_value_type = std::add_const_t; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = general_span_iterator; + using const_strided_span = strided_span; + using const_iterator = general_span_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = + std::conditional_t>; + +private: + pointer data_; + bounds_type bounds_; + + friend iterator; + friend const_iterator; + template + friend class strided_span; + +public: + // from raw data + constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) + : data_(ptr), bounds_(std::move(bounds)) + { + Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); + // Bounds cross data boundaries + Expects(this->bounds().total_size() <= size); + (void) size; + } + + // from static array of size N + template + constexpr strided_span(value_type (&values)[N], bounds_type bounds) + : strided_span(values, N, std::move(bounds)) + { + } + + // from array view + template ::value, + typename Dummy = std::enable_if_t> + constexpr strided_span(multi_span av, bounds_type bounds) + : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) + { + } + + // convertible + template ::value>> + constexpr strided_span(const strided_span& other) + : data_(other.data_), bounds_(other.bounds_) + { + } + + // convert from bytes + template + constexpr strided_span< + typename std::enable_if::value, OtherValueType>::type, + Rank> + as_strided_span() const + { + static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && + (sizeof(OtherValueType) % sizeof(value_type) == 0), + "OtherValueType should have a size to contain a multiple of ValueTypes"); + auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); + + size_type size = this->bounds().total_size() / d; + return {const_cast(reinterpret_cast(this->data())), + size, bounds_type{resize_extent(this->bounds().index_bounds(), d), + resize_stride(this->bounds().strides(), d)}}; + } + + constexpr strided_span section(index_type origin, index_type extents) const + { + size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + return {&this->operator[](origin), size, + bounds_type{extents, details::make_stride(bounds())}}; + } + + constexpr reference operator[](const index_type& idx) const + { + return data_[bounds_.linearize(idx)]; + } + + template 1), typename Ret = std::enable_if_t> + constexpr Ret operator[](size_type idx) const + { + Expects(idx < bounds_.size()); // index is out of bounds of the array + const size_type ridx = idx * bounds_.stride(); + + // index is out of bounds of the underlying data + Expects(ridx < bounds_.total_size()); + return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; + } + + constexpr bounds_type bounds() const noexcept { return bounds_; } + + template + constexpr size_type extent() const noexcept + { + static_assert(Dim < Rank, + "dimension should be less than Rank (dimension count starts from 0)"); + return bounds_.template extent(); + } + + constexpr size_type size() const noexcept { return bounds_.size(); } + + constexpr pointer data() const noexcept { return data_; } + + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } + + constexpr iterator begin() const { return iterator{this, true}; } + + constexpr iterator end() const { return iterator{this, false}; } + + constexpr const_iterator cbegin() const + { + return const_iterator{reinterpret_cast(this), true}; + } + + constexpr const_iterator cend() const + { + return const_iterator{reinterpret_cast(this), false}; + } + + constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } + + constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } + + constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } + + template , std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const noexcept + { + return bounds_.size() == other.bounds_.size() && + (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const noexcept + { + return !(*this == other); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const noexcept + { + return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const noexcept + { + return !(other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const noexcept + { + return (other < *this); + } + + template , std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const noexcept + { + return !(*this < other); + } + +private: + static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) + { + // The last dimension of the array needs to contain a multiple of new type elements + Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); + + index_type ret = extent; + ret[Rank - 1] /= d; + + return ret; + } + + template > + static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + + return strides; + } + + template 1), typename Dummy = std::enable_if_t> + static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) + { + // Only strided arrays with regular strides can be resized + Expects(strides[Rank - 1] == 1); + // The strides must have contiguous chunks of + // memory that can contain a multiple of new type elements + Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); + + for (size_t i = Rank - 1; i > 0; --i) { + // Only strided arrays with regular strides can be resized + Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); + } + + index_type ret = strides / d; + ret[Rank - 1] = 1; + + return ret; + } +}; + +template +class contiguous_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + +private: + template + friend class multi_span; + + pointer data_; + const Span* m_validator; + void validateThis() const + { + // iterator is out of range of the array + Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); + } + contiguous_span_iterator(const Span* container, bool isbegin) + : data_(isbegin ? container->data_ : container->data_ + container->size()) + , m_validator(container) + { + } + +public: + reference operator*() const noexcept + { + validateThis(); + return *data_; + } + pointer operator->() const noexcept + { + validateThis(); + return data_; + } + contiguous_span_iterator& operator++() noexcept + { + ++data_; + return *this; + } + contiguous_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_span_iterator& operator--() noexcept + { + --data_; + return *this; + } + contiguous_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_span_iterator operator+(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret += n; + } + contiguous_span_iterator& operator+=(difference_type n) noexcept + { + data_ += n; + return *this; + } + contiguous_span_iterator operator-(difference_type n) const noexcept + { + contiguous_span_iterator ret{*this}; + return ret -= n; + } + contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ - rhs.data_; + } + reference operator[](difference_type n) const noexcept { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ == rhs.data_; + } + bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const contiguous_span_iterator& rhs) const noexcept + { + Expects(m_validator == rhs.m_validator); + return data_ < rhs.data_; + } + bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(contiguous_span_iterator& rhs) noexcept + { + std::swap(data_, rhs.data_); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, + const contiguous_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +template +class general_span_iterator + : public std::iterator +{ + using Base = std::iterator; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + +private: + template + friend class strided_span; + + const Span* m_container; + typename Span::bounds_type::iterator m_itr; + general_span_iterator(const Span* container, bool isbegin) + : m_container(container) + , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } + +public: + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() noexcept + { + ++m_itr; + return *this; + } + general_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + general_span_iterator& operator--() noexcept + { + --m_itr; + return *this; + } + general_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + general_span_iterator operator+(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret += n; + } + general_span_iterator& operator+=(difference_type n) noexcept + { + m_itr += n; + return *this; + } + general_span_iterator operator-(difference_type n) const noexcept + { + general_span_iterator ret{*this}; + return ret -= n; + } + general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + difference_type operator-(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; } + + bool operator==(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const noexcept + { + Expects(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(general_span_iterator& rhs) noexcept + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_span_iterator operator+(typename general_span_iterator::difference_type n, + const general_span_iterator& rhs) noexcept +{ + return rhs + n; +} + +} // namespace gsl + +#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_VARIADIC_CTOR_BUG + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#undef noexcept + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("noexcept") +#endif + +#endif // GSL_THROW_ON_CONTRACT_VIOLATION + +#endif // GSL_MULTI_SPAN_H diff --git a/gsl/multi_span.h b/gsl/multi_span.h deleted file mode 100644 index c883fb0..0000000 --- a/gsl/multi_span.h +++ /dev/null @@ -1,2239 +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_MULTI_SPAN_H -#define GSL_MULTI_SPAN_H - -#include "gsl_assert.h" -#include "gsl_byte.h" -#include "gsl_util.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(push) -#pragma warning(disable : 4127) // conditional expression is constant - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - -// noexcept is not understood -#ifndef GSL_THROW_ON_CONTRACT_VIOLATION -#pragma push_macro("noexcept") -#define noexcept /* nothing */ -#endif - -// turn off some misguided warnings -#pragma warning(push) -#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable : 4512) // warns that assignment op could not be generated - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#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 -{ - -/* -** begin definitions of index and bounds -*/ -namespace details -{ - template - struct SizeTypeTraits - { - static const SizeType max_value = std::numeric_limits::max(); - }; - - template - class are_integral : public std::integral_constant - { - }; - - template - class are_integral - : public std::integral_constant::value && are_integral::value> - { - }; -} - -template -class index final -{ - static_assert(Rank > 0, "Rank must be greater than 0!"); - - template - friend class index; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using size_type = value_type; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - - constexpr index() noexcept {} - - constexpr index(const value_type (&values)[Rank]) noexcept - { - std::copy(values, values + Rank, elems); - } - -#ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG - template < - typename T, typename... Ts, - typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral::value && - details::are_integral::value>> - constexpr index(T t, Ts... ds) - : index({narrow_cast(t), narrow_cast(ds)...}) - { - } -#else - template ::value>> - constexpr index(Ts... ds) noexcept : elems{narrow_cast(ds)...} - { - } -#endif - - constexpr index(const index& other) noexcept = default; - - constexpr index& operator=(const index& rhs) noexcept = default; - - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const noexcept - { - Expects(component_idx < Rank); // Component index must be less than rank - return elems[component_idx]; - } - - constexpr bool operator==(const index& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - - constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); } - - constexpr index operator+() const noexcept { return *this; } - - constexpr index operator-() const noexcept - { - index ret = *this; - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - - constexpr index operator+(const index& rhs) const noexcept - { - index ret = *this; - ret += rhs; - return ret; - } - - constexpr index operator-(const index& rhs) const noexcept - { - index ret = *this; - ret -= rhs; - return ret; - } - - constexpr index& operator+=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); - return *this; - } - - constexpr index& operator-=(const index& rhs) noexcept - { - std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); - return *this; - } - - constexpr index operator*(value_type v) const noexcept - { - index ret = *this; - ret *= v; - return ret; - } - - constexpr index operator/(value_type v) const noexcept - { - index ret = *this; - ret /= v; - return ret; - } - - friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; } - - constexpr index& operator*=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::multiplies{}(x, v); }); - return *this; - } - - constexpr index& operator/=(value_type v) noexcept - { - std::transform(elems, elems + rank, elems, - [v](value_type x) { return std::divides{}(x, v); }); - return *this; - } - -private: - value_type elems[Rank] = {}; -}; - -#ifndef _MSC_VER - -struct static_bounds_dynamic_range_t -{ - template ::value>> - constexpr operator T() const noexcept - { - return narrow_cast(-1); - } - - template ::value>> - constexpr bool operator==(T other) const noexcept - { - return narrow_cast(-1) == other; - } - - template ::value>> - constexpr bool operator!=(T other) const noexcept - { - return narrow_cast(-1) != other; - } -}; - -template ::value>> -constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right == left; -} - -template ::value>> -constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept -{ - return right != left; -} - -constexpr static_bounds_dynamic_range_t dynamic_range{}; -#else -const std::ptrdiff_t dynamic_range = -1; -#endif - -struct generalized_mapping_tag -{ -}; -struct contiguous_mapping_tag : generalized_mapping_tag -{ -}; - -namespace details -{ - - template - struct LessThan - { - static const bool value = Left < Right; - }; - - template - struct BoundsRanges - { - using size_type = std::ptrdiff_t; - static const size_type Depth = 0; - static const size_type DynamicNum = 0; - static const size_type CurrentRange = 1; - static const size_type TotalSize = 1; - - // TODO : following signature is for work around VS bug - template - BoundsRanges(const OtherRange&, bool /* firstLevel */) - { - } - - BoundsRanges(const BoundsRanges&) = default; - BoundsRanges& operator=(const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const) {} - BoundsRanges() = default; - - template - void serialize(T&) const - { - } - - template - size_type linearize(const T&) const - { - return 0; - } - - template - size_type contains(const T&) const - { - return -1; - } - - size_type elementNum(size_t) const noexcept { return 0; } - - size_type totalSize() const noexcept { return TotalSize; } - - bool operator==(const BoundsRanges&) const noexcept { return true; } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum + 1; - static const size_type CurrentRange = dynamic_range; - static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) - : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) - { - Expects(0 <= *arr); - } - - BoundsRanges() : m_bound(0) {} - - template - BoundsRanges(const BoundsRanges& other, - bool /* firstLevel */ = true) - : Base(static_cast&>(other), false) - , m_bound(other.totalSize()) - { - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - const size_type index = this->Base::totalSize() * arr[Dim]; - Expects(index < m_bound); - return index + this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - const ptrdiff_t last = this->Base::template contains(arr); - if (last == -1) return -1; - const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; - return cur < m_bound ? cur + last : -1; - } - - size_type totalSize() const noexcept { return m_bound; } - - size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return m_bound == rhs.m_bound && - static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRanges : BoundsRanges - { - using Base = BoundsRanges; - using size_type = std::ptrdiff_t; - static const size_t Depth = Base::Depth + 1; - static const size_t DynamicNum = Base::DynamicNum; - static const size_type CurrentRange = CurRange; - static const size_type TotalSize = - Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - - BoundsRanges(const BoundsRanges&) = default; - - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} - BoundsRanges() = default; - - template - BoundsRanges(const BoundsRanges& other, - bool firstLevel = true) - : Base(static_cast&>(other), false) - { - (void) firstLevel; - } - - template - void serialize(T& arr) const - { - arr[Dim] = elementNum(); - this->Base::template serialize(arr); - } - - template - size_type linearize(const T& arr) const - { - Expects(arr[Dim] < CurrentRange); // Index is out of range - return this->Base::totalSize() * arr[Dim] + - this->Base::template linearize(arr); - } - - template - size_type contains(const T& arr) const - { - if (arr[Dim] >= CurrentRange) return -1; - const size_type last = this->Base::template contains(arr); - if (last == -1) return -1; - return this->Base::totalSize() * arr[Dim] + last; - } - - size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); } - - size_type elementNum() const noexcept { return CurrentRange; } - - size_type elementNum(size_t dim) const noexcept - { - if (dim > 0) - return this->Base::elementNum(dim - 1); - else - return elementNum(); - } - - bool operator==(const BoundsRanges& rhs) const noexcept - { - return static_cast(*this) == static_cast(rhs); - } - }; - - template - struct BoundsRangeConvertible - : public std::integral_constant= TargetType::TotalSize || - TargetType::TotalSize == dynamic_range || - SourceType::TotalSize == dynamic_range || - TargetType::TotalSize == 0)> - { - }; - - template - struct TypeListIndexer - { - const TypeChain& obj_; - TypeListIndexer(const TypeChain& obj) : obj_(obj) {} - - template - const TypeChain& getObj(std::true_type) - { - return obj_; - } - - template - auto getObj(std::false_type) - -> decltype(TypeListIndexer(static_cast(obj_)).template get()) - { - return TypeListIndexer(static_cast(obj_)).template get(); - } - - template - auto get() -> decltype(getObj(std::integral_constant())) - { - return getObj(std::integral_constant()); - } - }; - - template - TypeListIndexer createTypeListIndexer(const TypeChain& obj) - { - return TypeListIndexer(obj); - } - - template 1), - typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const index& other) noexcept - { - Ret ret{}; - for (size_t i = 0; i < Rank - 1; ++i) { - ret[i] = other[i + 1]; - } - return ret; - } -} - -template -class bounds_iterator; - -template -class static_bounds -{ -public: - static_bounds(const details::BoundsRanges&) {} -}; - -template -class static_bounds -{ - using MyRanges = details::BoundsRanges; - - MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} - - template - friend class static_bounds; - -public: - static const size_t rank = MyRanges::Depth; - static const size_t dynamic_rank = MyRanges::DynamicNum; - static const std::ptrdiff_t static_size = MyRanges::TotalSize; - - using size_type = std::ptrdiff_t; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - using difference_type = std::ptrdiff_t; - using sliced_type = static_bounds; - using mapping_type = contiguous_mapping_tag; - - constexpr static_bounds(const static_bounds&) = default; - - template - struct BoundsRangeConvertible2; - - template > - static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; - - template - static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; - - template - struct BoundsRangeConvertible2 - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant())) - { - }; - - template - struct BoundsRangeConvertible2 : std::true_type - { - }; - - template - struct BoundsRangeConvertible - : decltype(helpBoundsRangeConvertible( - SourceType(), TargetType(), - std::integral_constant::value || - TargetType::CurrentRange == dynamic_range || - SourceType::CurrentRange == dynamic_range)>())) - { - }; - - template - struct BoundsRangeConvertible : std::true_type - { - }; - - template , - details::BoundsRanges>::value>> - constexpr static_bounds(const static_bounds& other) : m_ranges(other.m_ranges) - { - Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges::DynamicNum == 0) || - MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); - } - - constexpr static_bounds(std::initializer_list il) - : m_ranges(static_cast(il.begin())) - { - // Size of the initializer list must match the rank of the array - Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || - MyRanges::DynamicNum == il.size()); - // Size of the range must be less than the max element of the size type - Expects(m_ranges.totalSize() <= PTRDIFF_MAX); - } - - constexpr static_bounds() = default; - - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new (&m_ranges) MyRanges(otherBounds.m_ranges); - return *this; - } - - constexpr sliced_type slice() const noexcept - { - return sliced_type{static_cast&>(m_ranges)}; - } - - constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } - - constexpr size_type size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } - - constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - - constexpr bool contains(const index_type& idx) const noexcept - { - return m_ranges.contains(idx) != -1; - } - - constexpr size_type operator[](size_t index) const noexcept - { - return m_ranges.elementNum(index); - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < rank, - "dimension should be less than rank (dimension count starts from 0)"); - return details::createTypeListIndexer(m_ranges).template get().elementNum(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - static_assert(std::is_integral::value, - "Dimension parameter must be supplied as an integral type."); - auto real_dim = narrow_cast(dim); - Expects(real_dim < rank); - - return m_ranges.elementNum(real_dim); - } - - constexpr index_type index_bounds() const noexcept - { - size_type extents[rank] = {}; - m_ranges.serialize(extents); - return {extents}; - } - - template - constexpr bool operator==(const static_bounds& rhs) const noexcept - { - return this->size() == rhs.size(); - } - - template - constexpr bool operator!=(const static_bounds& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } - - constexpr const_iterator end() const noexcept - { - return const_iterator(*this, this->index_bounds()); - } -}; - -template -class strided_bounds -{ - template - friend class strided_bounds; - -public: - static const size_t rank = Rank; - using value_type = std::ptrdiff_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_const_t; - using size_type = value_type; - using difference_type = value_type; - using index_type = index; - using const_index_type = std::add_const_t; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; - static const value_type dynamic_rank = rank; - static const value_type static_size = dynamic_range; - using sliced_type = std::conditional_t, void>; - using mapping_type = generalized_mapping_tag; - - constexpr strided_bounds(const strided_bounds&) noexcept = default; - - constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; - - constexpr strided_bounds(const value_type (&values)[rank], index_type strides) - : m_extents(values), m_strides(std::move(strides)) - { - } - - constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept - : m_extents(extents), - m_strides(strides) - { - } - - constexpr index_type strides() const noexcept { return m_strides; } - - constexpr size_type total_size() const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; ++i) { - ret += (m_extents[i] - 1) * m_strides[i]; - } - return ret + 1; - } - - constexpr size_type size() const noexcept - { - size_type ret = 1; - for (size_t i = 0; i < rank; ++i) { - ret *= m_extents[i]; - } - return ret; - } - - constexpr bool contains(const index_type& idx) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; - } - return true; - } - - constexpr size_type linearize(const index_type& idx) const noexcept - { - size_type ret = 0; - for (size_t i = 0; i < rank; i++) { - Expects(idx[i] < m_extents[i]); // index is out of bounds of the array - ret += idx[i] * m_strides[i]; - } - return ret; - } - - constexpr size_type stride() const noexcept { return m_strides[0]; } - - template 1), typename Ret = std::enable_if_t> - constexpr sliced_type slice() const - { - return {details::shift_left(m_extents), details::shift_left(m_strides)}; - } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than rank (dimension count starts from 0)"); - return m_extents[Dim]; - } - - constexpr index_type index_bounds() const noexcept { return m_extents; } - constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } - - constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } - -private: - index_type m_extents; - index_type m_strides; -}; - -template -struct is_bounds : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; -template -struct is_bounds> : std::integral_constant -{ -}; - -template -class bounds_iterator : public std::iterator -{ -private: - using Base = std::iterator; - -public: - static const size_t rank = IndexType::rank; - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - using index_type = value_type; - using index_size_type = typename IndexType::value_type; - template - explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) - { - static_assert(is_bounds::value, "Bounds type must be provided"); - } - - constexpr reference operator*() const noexcept { return curr_; } - - constexpr pointer operator->() const noexcept { return &curr_; } - - constexpr bounds_iterator& operator++() noexcept - { - for (size_t i = rank; i-- > 0;) { - if (curr_[i] < boundary_[i] - 1) { - curr_[i]++; - return *this; - } - curr_[i] = 0; - } - // If we're here we've wrapped over - set to past-the-end. - curr_ = boundary_; - return *this; - } - - constexpr bounds_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr bounds_iterator& operator--() noexcept - { - if (!less(curr_, boundary_)) { - // if at the past-the-end, set to last element - for (size_t i = 0; i < rank; ++i) { - curr_[i] = boundary_[i] - 1; - } - return *this; - } - for (size_t i = rank; i-- > 0;) { - if (curr_[i] >= 1) { - curr_[i]--; - return *this; - } - curr_[i] = boundary_[i] - 1; - } - // If we're here the preconditions were violated - // "pre: there exists s such that r == ++s" - Expects(false); - return *this; - } - - constexpr bounds_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr bounds_iterator operator+(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret += n; - } - - constexpr bounds_iterator& operator+=(difference_type n) noexcept - { - auto linear_idx = linearize(curr_) + n; - std::remove_const_t stride = 0; - stride[rank - 1] = 1; - for (size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary_[i + 1]; - } - for (size_t i = 0; i < rank; ++i) { - curr_[i] = linear_idx / stride[i]; - linear_idx = linear_idx % stride[i]; - } - // index is out of bounds of the array - Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); - return *this; - } - - constexpr bounds_iterator operator-(difference_type n) const noexcept - { - bounds_iterator ret{*this}; - return ret -= n; - } - - constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - - constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept - { - return linearize(curr_) - linearize(rhs.curr_); - } - - constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const bounds_iterator& rhs) const noexcept - { - return curr_ == rhs.curr_; - } - - constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - - constexpr bool operator<(const bounds_iterator& rhs) const noexcept - { - return less(curr_, rhs.curr_); - } - - constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - - constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - - constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - - void swap(bounds_iterator& rhs) noexcept - { - std::swap(boundary_, rhs.boundary_); - std::swap(curr_, rhs.curr_); - } - -private: - constexpr bool less(index_type& one, index_type& other) const noexcept - { - for (size_t i = 0; i < rank; ++i) { - if (one[i] < other[i]) return true; - } - return false; - } - - constexpr index_size_type linearize(const value_type& idx) const noexcept - { - // TODO: Smarter impl. - // Check if past-the-end - index_size_type multiplier = 1; - index_size_type res = 0; - if (!less(idx, boundary_)) { - res = 1; - for (size_t i = rank; i-- > 0;) { - res += (idx[i] - 1) * multiplier; - multiplier *= boundary_[i]; - } - } - else - { - for (size_t i = rank; i-- > 0;) { - res += idx[i] * multiplier; - multiplier *= boundary_[i]; - } - } - return res; - } - - value_type boundary_; - std::remove_const_t curr_; -}; - -template -bounds_iterator operator+(typename bounds_iterator::difference_type n, - const bounds_iterator& rhs) noexcept -{ - return rhs + n; -} - -namespace details -{ - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - return bnd.strides(); - } - - // Make a stride vector from bounds, assuming contiguous memory. - template - constexpr std::enable_if_t< - std::is_same::value, - typename Bounds::index_type> - make_stride(const Bounds& bnd) noexcept - { - auto extents = bnd.index_bounds(); - typename Bounds::size_type stride[Bounds::rank] = {}; - - stride[Bounds::rank - 1] = 1; - for (size_t i = 1; i < Bounds::rank; ++i) { - stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; - } - return {stride}; - } - - template - void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) - { - static_assert(is_bounds::value && is_bounds::value, - "The src type and dest type must be bounds"); - static_assert(std::is_same::value, - "The source type must be a contiguous bounds"); - static_assert(BoundsDest::static_size == dynamic_range || - BoundsSrc::static_size == dynamic_range || - BoundsDest::static_size == BoundsSrc::static_size, - "The source bounds must have same size as dest bounds"); - Expects(src.size() == dest.size()); - } - -} // namespace details - -template -class contiguous_span_iterator; -template -class general_span_iterator; - -template -struct dim_t -{ - static const std::ptrdiff_t value = DimSize; -}; -template <> -struct dim_t -{ - static const std::ptrdiff_t value = dynamic_range; - const std::ptrdiff_t dvalue; - dim_t(std::ptrdiff_t size) : dvalue(size) {} -}; - -template -constexpr std::enable_if_t<(N >= 0),dim_t> dim() noexcept { - return dim_t(); -} - -template -constexpr std::enable_if_t> dim(std::ptrdiff_t n) noexcept { - return dim_t<>(n); -} - -template -class multi_span; - -template -class strided_span; - -namespace details -{ - template - struct SpanTypeTraits - { - using value_type = T; - using size_type = size_t; - }; - - template - struct SpanTypeTraits::type> - { - using value_type = typename Traits::span_traits::value_type; - using size_type = typename Traits::span_traits::size_type; - }; - - template - struct SpanArrayTraits - { - using type = multi_span; - using value_type = T; - using bounds_type = static_bounds; - using pointer = T*; - using reference = T&; - }; - template - struct SpanArrayTraits : SpanArrayTraits - { - }; - - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size - { - Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); - return BoundsType{totalSize}; - } - template - BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size - { - Expects(BoundsType::static_size <= totalSize); - return {}; - } - template - BoundsType newBoundsHelper(std::ptrdiff_t totalSize) - { - static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); - return newBoundsHelperImpl( - totalSize, std::integral_constant()); - } - - struct Sep - { - }; - - template - T static_as_multi_span_helper(Sep, Args... args) - { - return T{narrow_cast(args)...}; - } - template - std::enable_if_t< - !std::is_same>::value && !std::is_same::value, T> - static_as_multi_span_helper(Arg, Args... args) - { - return static_as_multi_span_helper(args...); - } - template - T static_as_multi_span_helper(dim_t val, Args... args) - { - return static_as_multi_span_helper(args..., val.dvalue); - } - - template - struct static_as_multi_span_static_bounds_helper - { - using type = static_bounds<(Dimensions::value)...>; - }; - - template - struct is_multi_span_oracle : std::false_type - { - }; - - template - struct is_multi_span_oracle> - : std::true_type - { - }; - - template - struct is_multi_span_oracle> : std::true_type - { - }; - - template - struct is_multi_span : is_multi_span_oracle> - { - }; -} - -template -class multi_span -{ - // TODO do we still need this? - template - friend class multi_span; - -public: - using bounds_type = static_bounds; - static const size_t Rank = bounds_type::rank; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = contiguous_span_iterator; - using const_span = multi_span; - using const_iterator = contiguous_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - -public: - // default constructor - same as constructing from nullptr_t - constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "Default construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr - get an empty multi_span - constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - } - - // construct from nullptr with size of 0 (helps with template function calls) - template ::value>> - constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{}) - { - static_assert(bounds_type::dynamic_rank != 0 || - (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), - "nullptr_t construction of multi_span only possible " - "for dynamic or fixed, zero-length spans."); - Expects(size == 0); - } - - // construct from a single element - constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1}) - { - static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || - bounds_type::static_size == 1, - "Construction from a single element only possible " - "for dynamic or fixed spans of length 0 or 1."); - } - - // prevent constructing from temporaries for single-elements - constexpr multi_span(value_type&&) = delete; - - // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size}) - { - } - - // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data), - bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); - } - - // construct from begin,end pointer pair - template ::value && - details::LessThan::value>> - constexpr multi_span(pointer begin, Ptr end) - : multi_span(begin, - details::newBoundsHelper(static_cast(end) - begin)) - { - Expects(begin != nullptr && end != nullptr && begin <= static_cast(end)); - } - - // construct from n-dimensions static array - template > - constexpr multi_span(T (&arr)[N]) - : multi_span(reinterpret_cast(arr), bounds_type{typename Helper::bounds_type{}}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible::value, - "Cannot construct a multi_span from an array with fewer elements."); - } - - // construct from n-dimensions dynamic array (e.g. new int[m][4]) - // (precedence will be lower than the 1-dimension pointer) - template > - constexpr multi_span(T* const& data, size_type size) - : multi_span(reinterpret_cast(data), typename Helper::bounds_type{size}) - { - static_assert(std::is_convertible::value, - "Cannot convert from source type to target multi_span type."); - } - - // construct from std::array - template - constexpr multi_span(std::array& arr) - : multi_span(arr.data(), bounds_type{static_bounds{}}) - { - static_assert( - std::is_convertible(*)[]>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // construct from const std::array - template - constexpr multi_span(const std::array, N>& arr) - : multi_span(arr.data(), static_bounds()) - { - static_assert(std::is_convertible>::value, - "Cannot convert from source type to target multi_span type."); - static_assert(std::is_convertible, bounds_type>::value, - "You cannot construct a multi_span from a std::array of smaller size."); - } - - // prevent constructing from temporary std::array - template - constexpr multi_span(std::array&& arr) = delete; - - // construct from containers - // future: could use contiguous_iterator_traits to identify only contiguous containers - // type-requirements: container must have .size(), operator[] which are value_type compatible - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - constexpr multi_span(Cont& cont) - : multi_span(static_cast(cont.data()), - details::newBoundsHelper(narrow_cast(cont.size()))) - { - } - - // prevent constructing from temporary containers - template ::value && - std::is_convertible::value && - std::is_same().size(), - *std::declval().data())>, - DataType>::value>> - explicit constexpr multi_span(Cont&& cont) = delete; - - // construct from a convertible multi_span - template , - typename = std::enable_if_t::value && - std::is_convertible::value>> - constexpr multi_span(multi_span other) noexcept - : data_(other.data_), - bounds_(other.bounds_) - { - } - -// trivial copy and move -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span(multi_span&&) = default; -#endif - constexpr multi_span(const multi_span&) = default; - -// trivial assignment -#ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT - constexpr multi_span& operator=(multi_span&&) = default; -#endif - constexpr multi_span& operator=(const multi_span&) = default; - - // first() - extract the first Count elements into a new multi_span - template - constexpr multi_span first() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data(), Count}; - } - - // first() - extract the first count elements into a new multi_span - constexpr multi_span first(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data(), count}; - } - - // last() - extract the last Count elements into a new multi_span - template - constexpr multi_span last() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - Count <= bounds_type::static_size, - "Count is out of bounds."); - - Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); - return {this->data() + this->size() - Count, Count}; - } - - // last() - extract the last count elements into a new multi_span - constexpr multi_span last(size_type count) const noexcept - { - Expects(count >= 0 && count <= this->size()); - return {this->data() + this->size() - count, count}; - } - - // subspan() - create a subview of Count elements starting at Offset - template - constexpr multi_span subspan() const noexcept - { - static_assert(Count >= 0, "Count must be >= 0."); - static_assert(Offset >= 0, "Offset must be >= 0."); - static_assert(bounds_type::static_size == dynamic_range || - ((Offset <= bounds_type::static_size) && - Count <= bounds_type::static_size - Offset), - "You must describe a sub-range within bounds of the multi_span."); - - Expects(bounds_type::static_size != dynamic_range || - (Offset <= this->size() && Count <= this->size() - Offset)); - return {this->data() + Offset, Count}; - } - - // subspan() - create a subview of count elements starting at offset - // supplying dynamic_range for count will consume all available elements from offset - constexpr multi_span subspan(size_type offset, - size_type count = dynamic_range) const - noexcept - { - Expects((offset >= 0 && offset <= this->size()) && - (count == dynamic_range || (count <= this->size() - offset))); - return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; - } - - // section - creates a non-contiguous, strided multi_span from a contiguous one - constexpr strided_span section(index_type origin, index_type extents) const - noexcept - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - strided_bounds{extents, details::make_stride(bounds())}}; - } - - // length of the multi_span in elements - constexpr size_type size() const noexcept { return bounds_.size(); } - - // length of the multi_span in elements - constexpr size_type length() const noexcept { return this->size(); } - - // length of the multi_span in bytes - constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); } - - // length of the multi_span in bytes - constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } - - constexpr bool empty() const noexcept { return this->size() == 0; } - - static constexpr std::size_t rank() { return Rank; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "Dimension should be less than rank (dimension count starts from 0)."); - return bounds_.template extent(); - } - - template - constexpr size_type extent(IntType dim) const noexcept - { - return bounds_.extent(dim); - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - constexpr pointer data() const noexcept { return data_; } - - template - constexpr reference operator()(FirstIndex index) - { - return this->operator[](narrow_cast(index)); - } - - template - constexpr reference operator()(FirstIndex index, OtherIndices... indices) - { - index_type idx = {narrow_cast(index), - narrow_cast(indices...)}; - return this->operator[](idx); - } - - constexpr reference operator[](const index_type& idx) const noexcept - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const noexcept - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return Ret{data_ + ridx, bounds_.slice()}; - } - - constexpr iterator begin() const noexcept { return iterator{this, true}; } - - constexpr iterator end() const noexcept { return iterator{this, false}; } - - constexpr const_iterator cbegin() const noexcept - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const noexcept - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const noexcept - { - return const_reverse_iterator{cend()}; - } - - constexpr const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator{cbegin()}; - } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const multi_span& other) const - noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const multi_span& other) const - noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const multi_span& other) const - noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const multi_span& other) const - noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const multi_span& other) const - noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const multi_span& other) const - noexcept - { - return !(*this < other); - } -}; - -// -// Free functions for manipulating spans -// - -// reshape a multi_span into a different dimensionality -// DimCount and Enabled here are workarounds for a bug in MSVC 2015 -template 0), typename = std::enable_if_t> -constexpr auto as_multi_span(SpanType s, Dimensions2... dims) - -> multi_span -{ - static_assert(details::is_multi_span::value, - "Variadic as_multi_span() is for reshaping existing spans."); - using BoundsType = - typename multi_span::bounds_type; - auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); - details::verifyBoundsReshape(s.bounds(), tobounds); - return {s.data(), tobounds}; -} - -// convert a multi_span to a multi_span -template -multi_span as_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span (a writeable byte multi_span) -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -multi_span as_writeable_bytes(multi_span s) noexcept -{ - static_assert(std::is_trivial>::value, - "The value_type of multi_span must be a trivial type."); - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_multi_span(multi_span s) noexcept -> multi_span< - const U, static_cast( - multi_span::bounds_type::static_size != dynamic_range - ? (static_cast( - multi_span::bounds_type::static_size) / - sizeof(U)) - : dynamic_range)> -{ - using ConstByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ConstByteSpan::bounds_type::static_size == dynamic_range || - ConstByteSpan::bounds_type::static_size % narrow_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -// convert a multi_span to a multi_span -// this is not currently a portable function that can be relied upon to work -// on all implementations. It should be considered an experimental extension -// to the standard GSL interface. -template -constexpr auto as_multi_span(multi_span s) noexcept - -> multi_span( - multi_span::bounds_type::static_size != dynamic_range - ? static_cast( - multi_span::bounds_type::static_size) / - sizeof(U) - : dynamic_range)> -{ - using ByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ByteSpan::bounds_type::static_size == dynamic_range || - ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); - - Expects((s.size_bytes() % sizeof(U)) == 0); - return {reinterpret_cast(s.data()), - s.size_bytes() / narrow_cast(sizeof(U))}; -} - -template -constexpr auto as_multi_span(T* const& ptr, dim_t... args) - -> multi_span, Dimensions...> -{ - return {reinterpret_cast*>(ptr), - details::static_as_multi_span_helper>(args..., - details::Sep{})}; -} - -template -constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> - typename details::SpanArrayTraits::type -{ - return {reinterpret_cast*>(arr), len}; -} - -template -constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits::type -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(const std::array&&) = delete; - -template -constexpr multi_span as_multi_span(std::array& arr) -{ - return {arr}; -} - -template -constexpr multi_span as_multi_span(T* begin, T* end) -{ - return {begin, end}; -} - -template -constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> -{ - Expects(arr.size() < PTRDIFF_MAX); - return {arr.data(), narrow_cast(arr.size())}; -} - -template -constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< - !details::is_multi_span>::value, - multi_span, dynamic_range>> = delete; - -// from basic_string which doesn't have nonconst .data() member like other contiguous containers -template -constexpr auto as_multi_span(std::basic_string& str) - -> multi_span -{ - Expects(str.size() < PTRDIFF_MAX); - return {&str[0], narrow_cast(str.size())}; -} - -// strided_span is an extension that is not strictly part of the GSL at this time. -// It is kept here while the multidimensional interface is still being defined. -template -class strided_span -{ -public: - using bounds_type = strided_bounds; - using size_type = typename bounds_type::size_type; - using index_type = typename bounds_type::index_type; - using value_type = ValueType; - using const_value_type = std::add_const_t; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = general_span_iterator; - using const_strided_span = strided_span; - using const_iterator = general_span_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using sliced_type = - std::conditional_t>; - -private: - pointer data_; - bounds_type bounds_; - - friend iterator; - friend const_iterator; - template - friend class strided_span; - -public: - // from raw data - constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) - : data_(ptr), bounds_(std::move(bounds)) - { - Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); - // Bounds cross data boundaries - Expects(this->bounds().total_size() <= size); - (void) size; - } - - // from static array of size N - template - constexpr strided_span(value_type (&values)[N], bounds_type bounds) - : strided_span(values, N, std::move(bounds)) - { - } - - // from array view - template ::value, - typename Dummy = std::enable_if_t> - constexpr strided_span(multi_span av, bounds_type bounds) - : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - { - } - - // convertible - template ::value>> - constexpr strided_span(const strided_span& other) - : data_(other.data_), bounds_(other.bounds_) - { - } - - // convert from bytes - template - constexpr strided_span< - typename std::enable_if::value, OtherValueType>::type, - Rank> - as_strided_span() const - { - static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && - (sizeof(OtherValueType) % sizeof(value_type) == 0), - "OtherValueType should have a size to contain a multiple of ValueTypes"); - auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); - - size_type size = this->bounds().total_size() / d; - return {const_cast(reinterpret_cast(this->data())), - size, bounds_type{resize_extent(this->bounds().index_bounds(), d), - resize_stride(this->bounds().strides(), d)}}; - } - - constexpr strided_span section(index_type origin, index_type extents) const - { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); - return {&this->operator[](origin), size, - bounds_type{extents, details::make_stride(bounds())}}; - } - - constexpr reference operator[](const index_type& idx) const - { - return data_[bounds_.linearize(idx)]; - } - - template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const - { - Expects(idx < bounds_.size()); // index is out of bounds of the array - const size_type ridx = idx * bounds_.stride(); - - // index is out of bounds of the underlying data - Expects(ridx < bounds_.total_size()); - return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; - } - - constexpr bounds_type bounds() const noexcept { return bounds_; } - - template - constexpr size_type extent() const noexcept - { - static_assert(Dim < Rank, - "dimension should be less than Rank (dimension count starts from 0)"); - return bounds_.template extent(); - } - - constexpr size_type size() const noexcept { return bounds_.size(); } - - constexpr pointer data() const noexcept { return data_; } - - constexpr explicit operator bool() const noexcept { return data_ != nullptr; } - - constexpr iterator begin() const { return iterator{this, true}; } - - constexpr iterator end() const { return iterator{this, false}; } - - constexpr const_iterator cbegin() const - { - return const_iterator{reinterpret_cast(this), true}; - } - - constexpr const_iterator cend() const - { - return const_iterator{reinterpret_cast(this), false}; - } - - constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } - - constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } - - constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } - - constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } - - template , std::remove_cv_t>::value>> - constexpr bool operator==(const strided_span& other) const noexcept - { - return bounds_.size() == other.bounds_.size() && - (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator!=(const strided_span& other) const noexcept - { - return !(*this == other); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<(const strided_span& other) const noexcept - { - return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator<=(const strided_span& other) const noexcept - { - return !(other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>(const strided_span& other) const noexcept - { - return (other < *this); - } - - template , std::remove_cv_t>::value>> - constexpr bool operator>=(const strided_span& other) const noexcept - { - return !(*this < other); - } - -private: - static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) - { - // The last dimension of the array needs to contain a multiple of new type elements - Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); - - index_type ret = extent; - ret[Rank - 1] /= d; - - return ret; - } - - template > - static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - - return strides; - } - - template 1), typename Dummy = std::enable_if_t> - static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) - { - // Only strided arrays with regular strides can be resized - Expects(strides[Rank - 1] == 1); - // The strides must have contiguous chunks of - // memory that can contain a multiple of new type elements - Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - - for (size_t i = Rank - 1; i > 0; --i) { - // Only strided arrays with regular strides can be resized - Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); - } - - index_type ret = strides / d; - ret[Rank - 1] = 1; - - return ret; - } -}; - -template -class contiguous_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - -private: - template - friend class multi_span; - - pointer data_; - const Span* m_validator; - void validateThis() const - { - // iterator is out of range of the array - Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); - } - contiguous_span_iterator(const Span* container, bool isbegin) - : data_(isbegin ? container->data_ : container->data_ + container->size()) - , m_validator(container) - { - } - -public: - reference operator*() const noexcept - { - validateThis(); - return *data_; - } - pointer operator->() const noexcept - { - validateThis(); - return data_; - } - contiguous_span_iterator& operator++() noexcept - { - ++data_; - return *this; - } - contiguous_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - contiguous_span_iterator& operator--() noexcept - { - --data_; - return *this; - } - contiguous_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - contiguous_span_iterator operator+(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret += n; - } - contiguous_span_iterator& operator+=(difference_type n) noexcept - { - data_ += n; - return *this; - } - contiguous_span_iterator operator-(difference_type n) const noexcept - { - contiguous_span_iterator ret{*this}; - return ret -= n; - } - contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ - rhs.data_; - } - reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ == rhs.data_; - } - bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const contiguous_span_iterator& rhs) const noexcept - { - Expects(m_validator == rhs.m_validator); - return data_ < rhs.data_; - } - bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(contiguous_span_iterator& rhs) noexcept - { - std::swap(data_, rhs.data_); - std::swap(m_validator, rhs.m_validator); - } -}; - -template -contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, - const contiguous_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -template -class general_span_iterator - : public std::iterator -{ - using Base = std::iterator; - -public: - using typename Base::reference; - using typename Base::pointer; - using typename Base::difference_type; - using typename Base::value_type; - -private: - template - friend class strided_span; - - const Span* m_container; - typename Span::bounds_type::iterator m_itr; - general_span_iterator(const Span* container, bool isbegin) - : m_container(container) - , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } - -public: - reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() noexcept { return &(*m_container)[*m_itr]; } - general_span_iterator& operator++() noexcept - { - ++m_itr; - return *this; - } - general_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - general_span_iterator& operator--() noexcept - { - --m_itr; - return *this; - } - general_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - general_span_iterator operator+(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret += n; - } - general_span_iterator& operator+=(difference_type n) noexcept - { - m_itr += n; - return *this; - } - general_span_iterator operator-(difference_type n) const noexcept - { - general_span_iterator ret{*this}; - return ret -= n; - } - general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr - rhs.m_itr; - } - value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; } - - bool operator==(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr == rhs.m_itr; - } - bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const general_span_iterator& rhs) const noexcept - { - Expects(m_container == rhs.m_container); - return m_itr < rhs.m_itr; - } - bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(general_span_iterator& rhs) noexcept - { - std::swap(m_itr, rhs.m_itr); - std::swap(m_container, rhs.m_container); - } -}; - -template -general_span_iterator operator+(typename general_span_iterator::difference_type n, - const general_span_iterator& rhs) noexcept -{ - return rhs + n; -} - -} // namespace gsl - -#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_VARIADIC_CTOR_BUG - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) - -#undef noexcept - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pop_macro("noexcept") -#endif - -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - -#endif // GSL_MULTI_SPAN_H diff --git a/gsl/span b/gsl/span new file mode 100644 index 0000000..b6a2f6d --- /dev/null +++ b/gsl/span @@ -0,0 +1,826 @@ + +/////////////////////////////////////////////////////////////////////////////// +// +// 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_SPAN_H +#define GSL_SPAN_H + +#include "gsl_assert" +#include "gsl_byte" +#include "gsl_util" +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR +#define GSL_MSVC_NO_CPP14_STD_EQUAL + +// noexcept is not understood +#ifndef GSL_THROW_ON_CONTRACT_VIOLATION +#pragma push_macro("noexcept") +#define noexcept /* nothing */ +#endif + +#pragma push_macro("alignof") +#define alignof __alignof + +// turn off some misguided warnings +#pragma warning(push) +#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior +#pragma warning(disable : 4512) // warns that assignment op could not be generated + +#endif // _MSC_VER <= 1800 + +#endif // _MSC_VER + +#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 +{ + +// [views.constants], constants +constexpr const std::ptrdiff_t dynamic_extent = -1; + +template +class span; + +// implementation details +namespace details +{ + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : public is_std_array_oracle> + { + }; + + template + struct is_allowed_pointer_conversion + : public std::integral_constant::value && + std::is_pointer::value && + std::is_convertible::value> + { + }; + + template + struct is_allowed_integral_conversion + : public std::integral_constant< + bool, std::is_integral::value && std::is_integral::value && + sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && + std::is_convertible::value> + { + }; + + template + struct is_allowed_extent_conversion + : public std::integral_constant + { + }; + + template + struct is_allowed_element_type_conversion + : public std::integral_constant>::value || + is_allowed_pointer_conversion::value || + is_allowed_integral_conversion::value> + { + }; + + template + struct is_allowed_element_type_conversion + : public std::integral_constant::value> + { + }; + + template + struct is_allowed_element_type_conversion : public std::true_type + { + }; + + template + class const_span_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using const_pointer = std::add_const_t; + using pointer = const_pointer; + + using const_reference = std::add_const_t; + using reference = const_reference; + + constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} + constexpr const_span_iterator(const Span* span, typename Span::index_type index) + : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } + + constexpr reference operator*() const + { + Expects(span_); + return (*span_)[index_]; + } + + constexpr pointer operator->() const + { + Expects(span_); + return &((*span_)[index_]); + } + + constexpr const_span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } + + constexpr const_span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr const_span_iterator& operator--() noexcept + { + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; + return *this; + } + + constexpr const_span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr const_span_iterator operator+(difference_type n) const noexcept + { + auto ret = *this; + return ret += n; + } + + constexpr const_span_iterator& operator+=(difference_type n) noexcept + { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; + } + + constexpr const_span_iterator operator-(difference_type n) const noexcept + { + auto ret = *this; + return ret -= n; + } + + constexpr const_span_iterator& operator-=(difference_type n) noexcept + { + return *this += -n; + } + + constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const const_span_iterator& rhs) const noexcept + { + return span_ == rhs.span_ && index_ == rhs.index_; + } + + constexpr bool operator!=(const const_span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const const_span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } + + constexpr bool operator<=(const const_span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const const_span_iterator& rhs) const noexcept + { + return rhs < *this; + } + + constexpr bool operator>=(const const_span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(const_span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(span_, rhs.span_); + } + + private: + const Span* span_; + std::ptrdiff_t index_; + }; + + template + class span_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = typename Span::element_type; + using difference_type = std::ptrdiff_t; + + using pointer = value_type*; + using reference = value_type&; + + constexpr span_iterator() : span_iterator(nullptr, 0) {} + constexpr span_iterator(const Span* span, typename Span::index_type index) + : span_(span), index_(index) + { + Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); + } + + constexpr reference operator*() const + { + Expects(span_); + return (*span_)[index_]; + } + + constexpr pointer operator->() const + { + Expects(span_); + return &((*span_)[index_]); + } + + constexpr span_iterator& operator++() noexcept + { + Expects(span_ && index_ >= 0 && index_ < span_->length()); + ++index_; + return *this; + } + + constexpr span_iterator operator++(int) noexcept + { + auto ret = *this; + ++(*this); + return ret; + } + + constexpr span_iterator& operator--() noexcept + { + Expects(span_ && index_ > 0 && index_ <= span_->length()); + --index_; + return *this; + } + + constexpr span_iterator operator--(int) noexcept + { + auto ret = *this; + --(*this); + return ret; + } + + constexpr span_iterator operator+(difference_type n) const noexcept + { + auto ret = *this; + return ret += n; + } + + constexpr span_iterator& operator+=(difference_type n) noexcept + { + Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); + index_ += n; + return *this; + } + + constexpr span_iterator operator-(difference_type n) const noexcept + { + auto ret = *this; + return ret -= n; + } + + constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + + constexpr difference_type operator-(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ - rhs.index_; + } + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } + + constexpr bool operator==(const span_iterator& rhs) const noexcept + { + return span_ == rhs.span_ && index_ == rhs.index_; + } + + constexpr bool operator!=(const span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + constexpr bool operator<(const span_iterator& rhs) const noexcept + { + Expects(span_ == rhs.span_); + return index_ < rhs.index_; + } + + constexpr bool operator<=(const span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } + + constexpr bool operator>=(const span_iterator& rhs) const noexcept + { + return !(rhs > *this); + } + + void swap(span_iterator& rhs) noexcept + { + std::swap(index_, rhs.index_); + std::swap(span_, rhs.span_); + } + + private: + const Span* span_; + std::ptrdiff_t index_; + }; + + template + constexpr const_span_iterator + operator+(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs + n; + } + + template + constexpr const_span_iterator + operator-(typename const_span_iterator::difference_type n, + const const_span_iterator& rhs) noexcept + { + return rhs - n; + } + + template + constexpr span_iterator operator+(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs + n; + } + + template + constexpr span_iterator operator-(typename span_iterator::difference_type n, + const span_iterator& rhs) noexcept + { + return rhs - n; + } + + template + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); + + constexpr extent_type() noexcept {} + + template + constexpr extent_type(extent_type ext) noexcept + { + static_assert(Other == Ext || Other == dynamic_extent, + "Mismatch between fixed-size extent and size of initializing data."); + Expects(ext.size() == Ext); + } + + constexpr extent_type(index_type size) { Expects(size == Ext); } + + constexpr inline index_type size() const noexcept { return Ext; } + }; + + template <> + class extent_type + { + public: + using index_type = std::ptrdiff_t; + + template + explicit constexpr extent_type(extent_type ext) : size_(ext.size()) + { + } + + explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } + + constexpr inline index_type size() const noexcept { return size_; } + + private: + index_type size_; + }; +} // namespace details + +// [span], class template span +template +class span +{ +public: + // constants and types + using element_type = ElementType; + using index_type = std::ptrdiff_t; + using pointer = element_type*; + using reference = element_type&; + + using iterator = details::span_iterator>; + using const_iterator = details::const_span_iterator>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr static const index_type extent = Extent; + + // [span.cons], span constructors, copy, assignment, and destructor + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} + + constexpr span(std::nullptr_t) noexcept : span() {} + + constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} + + constexpr span(pointer firstElem, pointer lastElem) + : storage_(firstElem, std::distance(firstElem, lastElem)) + { + } + + template + constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) + { + } + + template > + constexpr span(std::array& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + template + constexpr span(const std::array, N>& arr) noexcept + : storage_(&arr[0], details::extent_type()) + { + } + + // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement + // on Container to be a contiguous sequence container. + template ::value && !details::is_std_array::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(Container& cont) : span(cont.data(), cont.size()) + { + } + + template ::value && !details::is_span::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr span(const Container& cont) : span(cont.data(), cont.size()) + { + } + + constexpr span(const span& other) noexcept = default; +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr span(span&& other) noexcept = default; +#else + constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {} +#endif + + template < + class OtherElementType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> + constexpr span(const span& other) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) + { + } + + template < + class OtherElementType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t< + details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value>> + constexpr span(span&& other) + : storage_(reinterpret_cast(other.data()), + details::extent_type(other.size())) + { + } + + ~span() noexcept = default; + constexpr span& operator=(const span& other) noexcept = default; + +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr span& operator=(span&& other) noexcept = default; +#else + constexpr span& operator=(span&& other) noexcept + { + storage_ = std::move(other.storage_); + return *this; + } +#endif + // [span.sub], span subviews + template + constexpr span first() const + { + Expects(Count >= 0 && Count <= size()); + return {data(), Count}; + } + + template + constexpr span last() const + { + Expects(Count >= 0 && Count <= size()); + return {data() + (size() - Count), Count}; + } + + template + constexpr span subspan() const + { + Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && + (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); + return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; + } + + constexpr span first(index_type count) const + { + Expects(count >= 0 && count <= size()); + return {data(), count}; + } + + constexpr span last(index_type count) const + { + Expects(count >= 0 && count <= size()); + return {data() + (size() - count), count}; + } + + constexpr span subspan(index_type offset, + index_type count = dynamic_extent) const + { + Expects((offset == 0 || (offset > 0 && offset <= size())) && + (count == dynamic_extent || (count >= 0 && offset + count <= size()))); + return {data() + offset, count == dynamic_extent ? size() - offset : count}; + } + + // [span.obs], span observers + constexpr index_type length() const noexcept { return size(); } + constexpr index_type size() const noexcept { return storage_.size(); } + constexpr index_type length_bytes() const noexcept { return size_bytes(); } + constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + constexpr reference operator[](index_type idx) const + { + Expects(idx >= 0 && idx < storage_.size()); + return data()[idx]; + } + constexpr reference operator()(index_type idx) const { return this->operator[](idx); } + constexpr pointer data() const noexcept { return storage_.data(); } + + // [span.iter], span iterator support + iterator begin() const noexcept { return {this, 0}; } + iterator end() const noexcept { return {this, length()}; } + + const_iterator cbegin() const noexcept { return {this, 0}; } + const_iterator cend() const noexcept { return {this, length()}; } + + reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } + +private: + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + template + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) + { + Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); + } + + constexpr inline pointer data() const noexcept { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; +}; + +// [span.comparison], span comparison operators +template +constexpr bool operator==(const span& l, + const span& r) +{ +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin()); +#else + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +#endif +} + +template +constexpr bool operator!=(const span& l, const span& r) +{ + return !(l == r); +} + +template +constexpr bool operator<(const span& l, const span& r) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator<=(const span& l, const span& r) +{ + return !(l > r); +} + +template +constexpr bool operator>(const span& l, const span& r) +{ + return r < l; +} + +template +constexpr bool operator>=(const span& l, const span& r) +{ + return !(l < r); +} + +namespace details +{ + // if we only supported compilers with good constexpr support then + // this pair of classes could collapse down to a constexpr function + + // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as + // constexpr + // and so will fail compilation of the template + template + struct calculate_byte_size + : std::integral_constant(sizeof(ElementType) * + static_cast(Extent))> + { + }; + + template + struct calculate_byte_size + : std::integral_constant + { + }; +} + +// [span.objectrep], views of object representation +template +span::value> +as_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +span::value> +as_writeable_bytes(span s) noexcept +{ + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +} // namespace gsl + +#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 + +#pragma pop_macro("alignof") + +#undef GSL_MSVC_HAS_VARIADIC_CTOR_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 + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // GSL_SPAN_H diff --git a/gsl/span.h b/gsl/span.h deleted file mode 100644 index 1405aa9..0000000 --- a/gsl/span.h +++ /dev/null @@ -1,826 +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_SPAN_H -#define GSL_SPAN_H - -#include "gsl_assert.h" -#include "gsl_byte.h" -#include "gsl_util.h" -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER - -#pragma warning(push) - -// turn off some warnings that are noisy about our Expects statements -#pragma warning(disable : 4127) // conditional expression is constant - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_VARIADIC_CTOR_BUG -#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR -#define GSL_MSVC_NO_CPP14_STD_EQUAL - -// noexcept is not understood -#ifndef GSL_THROW_ON_CONTRACT_VIOLATION -#pragma push_macro("noexcept") -#define noexcept /* nothing */ -#endif - -#pragma push_macro("alignof") -#define alignof __alignof - -// turn off some misguided warnings -#pragma warning(push) -#pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior -#pragma warning(disable : 4512) // warns that assignment op could not be generated - -#endif // _MSC_VER <= 1800 - -#endif // _MSC_VER - -#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 -{ - -// [views.constants], constants -constexpr const std::ptrdiff_t dynamic_extent = -1; - -template -class span; - -// implementation details -namespace details -{ - template - struct is_span_oracle : std::false_type - { - }; - - template - struct is_span_oracle> : std::true_type - { - }; - - template - struct is_span : public is_span_oracle> - { - }; - - template - struct is_std_array_oracle : std::false_type - { - }; - - template - struct is_std_array_oracle> : std::true_type - { - }; - - template - struct is_std_array : public is_std_array_oracle> - { - }; - - template - struct is_allowed_pointer_conversion - : public std::integral_constant::value && - std::is_pointer::value && - std::is_convertible::value> - { - }; - - template - struct is_allowed_integral_conversion - : public std::integral_constant< - bool, std::is_integral::value && std::is_integral::value && - sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && - std::is_convertible::value> - { - }; - - template - struct is_allowed_extent_conversion - : public std::integral_constant - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value> - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant::value> - { - }; - - template - struct is_allowed_element_type_conversion : public std::true_type - { - }; - - template - class const_span_iterator - { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; - - using const_pointer = std::add_const_t; - using pointer = const_pointer; - - using const_reference = std::add_const_t; - using reference = const_reference; - - constexpr const_span_iterator() : const_span_iterator(nullptr, 0) {} - constexpr const_span_iterator(const Span* span, typename Span::index_type index) - : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } - - constexpr reference operator*() const - { - Expects(span_); - return (*span_)[index_]; - } - - constexpr pointer operator->() const - { - Expects(span_); - return &((*span_)[index_]); - } - - constexpr const_span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } - - constexpr const_span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr const_span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } - - constexpr const_span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr const_span_iterator operator+(difference_type n) const noexcept - { - auto ret = *this; - return ret += n; - } - - constexpr const_span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } - - constexpr const_span_iterator operator-(difference_type n) const noexcept - { - auto ret = *this; - return ret -= n; - } - - constexpr const_span_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - constexpr difference_type operator-(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const const_span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } - - constexpr bool operator!=(const const_span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const const_span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } - - constexpr bool operator<=(const const_span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const const_span_iterator& rhs) const noexcept - { - return rhs < *this; - } - - constexpr bool operator>=(const const_span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(const_span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(span_, rhs.span_); - } - - private: - const Span* span_; - std::ptrdiff_t index_; - }; - - template - class span_iterator - { - public: - using iterator_category = std::random_access_iterator_tag; - using value_type = typename Span::element_type; - using difference_type = std::ptrdiff_t; - - using pointer = value_type*; - using reference = value_type&; - - constexpr span_iterator() : span_iterator(nullptr, 0) {} - constexpr span_iterator(const Span* span, typename Span::index_type index) - : span_(span), index_(index) - { - Expects(span == nullptr || (index_ >= 0 && index <= span_->length())); - } - - constexpr reference operator*() const - { - Expects(span_); - return (*span_)[index_]; - } - - constexpr pointer operator->() const - { - Expects(span_); - return &((*span_)[index_]); - } - - constexpr span_iterator& operator++() noexcept - { - Expects(span_ && index_ >= 0 && index_ < span_->length()); - ++index_; - return *this; - } - - constexpr span_iterator operator++(int) noexcept - { - auto ret = *this; - ++(*this); - return ret; - } - - constexpr span_iterator& operator--() noexcept - { - Expects(span_ && index_ > 0 && index_ <= span_->length()); - --index_; - return *this; - } - - constexpr span_iterator operator--(int) noexcept - { - auto ret = *this; - --(*this); - return ret; - } - - constexpr span_iterator operator+(difference_type n) const noexcept - { - auto ret = *this; - return ret += n; - } - - constexpr span_iterator& operator+=(difference_type n) noexcept - { - Expects(span_ && (index_ + n) >= 0 && (index_ + n) <= span_->length()); - index_ += n; - return *this; - } - - constexpr span_iterator operator-(difference_type n) const noexcept - { - auto ret = *this; - return ret -= n; - } - - constexpr span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - - constexpr difference_type operator-(const span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ - rhs.index_; - } - - constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - - constexpr bool operator==(const span_iterator& rhs) const noexcept - { - return span_ == rhs.span_ && index_ == rhs.index_; - } - - constexpr bool operator!=(const span_iterator& rhs) const noexcept - { - return !(*this == rhs); - } - - constexpr bool operator<(const span_iterator& rhs) const noexcept - { - Expects(span_ == rhs.span_); - return index_ < rhs.index_; - } - - constexpr bool operator<=(const span_iterator& rhs) const noexcept - { - return !(rhs < *this); - } - - constexpr bool operator>(const span_iterator& rhs) const noexcept { return rhs < *this; } - - constexpr bool operator>=(const span_iterator& rhs) const noexcept - { - return !(rhs > *this); - } - - void swap(span_iterator& rhs) noexcept - { - std::swap(index_, rhs.index_); - std::swap(span_, rhs.span_); - } - - private: - const Span* span_; - std::ptrdiff_t index_; - }; - - template - constexpr const_span_iterator - operator+(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs + n; - } - - template - constexpr const_span_iterator - operator-(typename const_span_iterator::difference_type n, - const const_span_iterator& rhs) noexcept - { - return rhs - n; - } - - template - constexpr span_iterator operator+(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept - { - return rhs + n; - } - - template - constexpr span_iterator operator-(typename span_iterator::difference_type n, - const span_iterator& rhs) noexcept - { - return rhs - n; - } - - template - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - static_assert(Ext >= 0, "A fixed-size span must be >= 0 in size."); - - constexpr extent_type() noexcept {} - - template - constexpr extent_type(extent_type ext) noexcept - { - static_assert(Other == Ext || Other == dynamic_extent, - "Mismatch between fixed-size extent and size of initializing data."); - Expects(ext.size() == Ext); - } - - constexpr extent_type(index_type size) { Expects(size == Ext); } - - constexpr inline index_type size() const noexcept { return Ext; } - }; - - template <> - class extent_type - { - public: - using index_type = std::ptrdiff_t; - - template - explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } - - explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } - - constexpr inline index_type size() const noexcept { return size_; } - - private: - index_type size_; - }; -} // namespace details - -// [span], class template span -template -class span -{ -public: - // constants and types - using element_type = ElementType; - using index_type = std::ptrdiff_t; - using pointer = element_type*; - using reference = element_type&; - - using iterator = details::span_iterator>; - using const_iterator = details::const_span_iterator>; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - constexpr static const index_type extent = Extent; - - // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) {} - - constexpr span(std::nullptr_t) noexcept : span() {} - - constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} - - constexpr span(pointer firstElem, pointer lastElem) - : storage_(firstElem, std::distance(firstElem, lastElem)) - { - } - - template - constexpr span(element_type (&arr)[N]) noexcept : storage_(&arr[0], details::extent_type()) - { - } - - template > - constexpr span(std::array& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - template - constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } - - // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement - // on Container to be a contiguous sequence container. - template ::value && !details::is_std_array::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(Container& cont) : span(cont.data(), cont.size()) - { - } - - template ::value && !details::is_span::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr span(const Container& cont) : span(cont.data(), cont.size()) - { - } - - constexpr span(const span& other) noexcept = default; -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr span(span&& other) noexcept = default; -#else - constexpr span(span&& other) noexcept : storage_(std::move(other.storage_)) {} -#endif - - template < - class OtherElementType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t< - details::is_allowed_extent_conversion::value && - details::is_allowed_element_type_conversion::value>> - constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), - details::extent_type(other.size())) - { - } - - template < - class OtherElementType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t< - details::is_allowed_extent_conversion::value && - details::is_allowed_element_type_conversion::value>> - constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), - details::extent_type(other.size())) - { - } - - ~span() noexcept = default; - constexpr span& operator=(const span& other) noexcept = default; - -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr span& operator=(span&& other) noexcept = default; -#else - constexpr span& operator=(span&& other) noexcept - { - storage_ = std::move(other.storage_); - return *this; - } -#endif - // [span.sub], span subviews - template - constexpr span first() const - { - Expects(Count >= 0 && Count <= size()); - return {data(), Count}; - } - - template - constexpr span last() const - { - Expects(Count >= 0 && Count <= size()); - return {data() + (size() - Count), Count}; - } - - template - constexpr span subspan() const - { - Expects((Offset == 0 || (Offset > 0 && Offset <= size())) && - (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); - return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; - } - - constexpr span first(index_type count) const - { - Expects(count >= 0 && count <= size()); - return {data(), count}; - } - - constexpr span last(index_type count) const - { - Expects(count >= 0 && count <= size()); - return {data() + (size() - count), count}; - } - - constexpr span subspan(index_type offset, - index_type count = dynamic_extent) const - { - Expects((offset == 0 || (offset > 0 && offset <= size())) && - (count == dynamic_extent || (count >= 0 && offset + count <= size()))); - return {data() + offset, count == dynamic_extent ? size() - offset : count}; - } - - // [span.obs], span observers - constexpr index_type length() const noexcept { return size(); } - constexpr index_type size() const noexcept { return storage_.size(); } - constexpr index_type length_bytes() const noexcept { return size_bytes(); } - constexpr index_type size_bytes() const noexcept { return size() * sizeof(element_type); } - constexpr bool empty() const noexcept { return size() == 0; } - - // [span.elem], span element access - constexpr reference operator[](index_type idx) const - { - Expects(idx >= 0 && idx < storage_.size()); - return data()[idx]; - } - constexpr reference operator()(index_type idx) const { return this->operator[](idx); } - constexpr pointer data() const noexcept { return storage_.data(); } - - // [span.iter], span iterator support - iterator begin() const noexcept { return {this, 0}; } - iterator end() const noexcept { return {this, length()}; } - - const_iterator cbegin() const noexcept { return {this, 0}; } - const_iterator cend() const noexcept { return {this, length()}; } - - reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } - const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } - -private: - // this implementation detail class lets us take advantage of the - // empty base class optimization to pay for only storage of a single - // pointer in the case of fixed-size spans - template - class storage_type : public ExtentType - { - public: - template - constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) - { - Expects((!data && ExtentType::size() == 0) || (data && ExtentType::size() >= 0)); - } - - constexpr inline pointer data() const noexcept { return data_; } - - private: - pointer data_; - }; - - storage_type> storage_; -}; - -// [span.comparison], span comparison operators -template -constexpr bool operator==(const span& l, - const span& r) -{ -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (l.size() == r.size()) && std::equal(l.begin(), l.end(), r.begin()); -#else - return std::equal(l.begin(), l.end(), r.begin(), r.end()); -#endif -} - -template -constexpr bool operator!=(const span& l, const span& r) -{ - return !(l == r); -} - -template -constexpr bool operator<(const span& l, const span& r) -{ - return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); -} - -template -constexpr bool operator<=(const span& l, const span& r) -{ - return !(l > r); -} - -template -constexpr bool operator>(const span& l, const span& r) -{ - return r < l; -} - -template -constexpr bool operator>=(const span& l, const span& r) -{ - return !(l < r); -} - -namespace details -{ - // if we only supported compilers with good constexpr support then - // this pair of classes could collapse down to a constexpr function - - // we should use a narrow_cast<> to go to size_t, but older compilers may not see it as - // constexpr - // and so will fail compilation of the template - template - struct calculate_byte_size - : std::integral_constant(sizeof(ElementType) * - static_cast(Extent))> - { - }; - - template - struct calculate_byte_size - : std::integral_constant - { - }; -} - -// [span.objectrep], views of object representation -template -span::value> -as_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template ::value>> -span::value> -as_writeable_bytes(span s) noexcept -{ - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -} // namespace gsl - -#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 - -#pragma pop_macro("alignof") - -#undef GSL_MSVC_HAS_VARIADIC_CTOR_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 - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif // GSL_SPAN_H diff --git a/gsl/string_span b/gsl/string_span new file mode 100644 index 0000000..fa3d2ff --- /dev/null +++ b/gsl/string_span @@ -0,0 +1,828 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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" +#include "gsl_util" +#include "span" +#include +#include +#include + +#ifdef _MSC_VER + +// No MSVC does constexpr fully yet +#pragma push_macro("constexpr") +#define constexpr /* nothing */ + +#pragma warning(push) + +// blanket turn off warnings from CppCoreCheck for now +// so people aren't annoyed by them when running the tool. +// more targeted suppressions will be added in a future update to the GSL +#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG +#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#define GSL_MSVC_NO_CPP14_STD_EQUAL +#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +// 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 +using basic_zstring = CharT*; + +template +using czstring = basic_zstring; + +template +using cwzstring = basic_zstring; + +template +using zstring = basic_zstring; + +template +using wzstring = basic_zstring; + +// +// 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 +span 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 +inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) +{ + return ensure_sentinel(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const +// overloads to share an implementation +inline span ensure_z(char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(const char* const& sz, std::ptrdiff_t max) +{ + auto len = strnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) +{ + auto len = wcsnlen(sz, narrow_cast(max)); + Ensures(sz[len] == 0); + return {sz, static_cast(len)}; +} + +template +span ensure_z(T (&sz)[N]) +{ + return ensure_z(&sz[0], static_cast(N)); +} + +template +span::type, dynamic_extent> +ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), static_cast(cont.length())); +} + +template +class basic_string_span; + +namespace details +{ + template + struct is_basic_string_span_oracle : std::false_type + { + }; + + template + struct is_basic_string_span_oracle> : std::true_type + { + }; + + template + struct is_basic_string_span : is_basic_string_span_oracle> + { + }; + + template + struct length_func + { + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(strnlen(ptr, narrow_cast(length))); + } + }; + + template <> + struct length_func + { + std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept + { + return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + } + }; +} + +// +// string_span and relatives +// +template +class basic_string_span +{ +public: + using element_type = CharT; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; + using impl_type = span; + + using index_type = typename impl_type::index_type; + 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() noexcept = default; + + // copy + constexpr basic_string_span(const basic_string_span& other) noexcept = default; + +// move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span(basic_string_span&& other) noexcept = default; +#else + constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} +#endif + + // assign + constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; + +// move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; +#else + constexpr basic_string_span& operator=(basic_string_span&& other) noexcept + { + span_ = std::move(other.span_); + return *this; + } +#endif + + // from nullptr + constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {} + + constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} + constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} + + // From static arrays - if 0-terminated, remove 0 from the view + // All other containers allow 0s within the length, so we do not remove them + template + constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) + { + } + + template > + constexpr basic_string_span(std::array& arr) noexcept : span_(arr) + { + } + + template > + constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) + { + } + + // Container signature should work for basic_string after C++17 version exists + template + constexpr basic_string_span(std::basic_string& str) + : span_(&str[0], str.length()) + { + } + + template + constexpr basic_string_span(const std::basic_string& str) + : span_(&str[0], str.length()) + { + } + + // from containers. Containers must have a pointer type and data() function signatures + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(Container& cont) : span_(cont) + { + } + + template ::value && + std::is_convertible::value && + std::is_convertible().data())>::value>> + constexpr basic_string_span(const Container& cont) : span_(cont) + { + } + + // from string_span + template < + class OtherValueType, std::ptrdiff_t OtherExtent, + class = std::enable_if_t::impl_type, impl_type>::value>> + constexpr basic_string_span(basic_string_span other) + : span_(other.data(), other.length()) + { + } + + template + constexpr basic_string_span first() const + { + return {span_.template first()}; + } + + constexpr basic_string_span first(index_type count) const + { + return {span_.first(count)}; + } + + template + constexpr basic_string_span last() const + { + return {span_.template last()}; + } + + constexpr basic_string_span last(index_type count) const + { + return {span_.last(count)}; + } + + template + constexpr basic_string_span subspan() const + { + return {span_.template subspan()}; + } + + constexpr basic_string_span + subspan(index_type offset, index_type count = dynamic_extent) const + { + return {span_.subspan(offset, count)}; + } + + constexpr reference operator[](index_type idx) const { return span_[idx]; } + constexpr reference operator()(index_type idx) const { return span_[idx]; } + + constexpr pointer data() const { return span_.data(); } + + constexpr index_type length() const noexcept { return span_.size(); } + constexpr index_type size() const noexcept { return span_.size(); } + constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } + constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr bool empty() const noexcept { return size() == 0; } + + 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 { return 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) + { + return {sz, details::length_func()(sz, max)}; + } + + template + static impl_type remove_z(element_type (&sz)[N]) + { + return remove_z(&sz[0], narrow_cast(N)); + } + + impl_type span_; +}; + +template +using string_span = basic_string_span; + +template +using cstring_span = basic_string_span; + +template +using wstring_span = basic_string_span; + +template +using cwstring_span = basic_string_span; + +// +// to_string() allow (explicit) conversions from string_span to string +// +#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG + +template +std::basic_string::type> +to_string(basic_string_span view) +{ + return {view.data(), static_cast(view.length())}; +} + +#else + +inline std::string to_string(cstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::string to_string(string_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::wstring to_string(cwstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +inline std::wstring to_string(wstring_span<> view) +{ + return {view.data(), static_cast(view.length())}; +} + +#endif + +// zero-terminated string span, used to convert +// zero-terminated spans to legacy strings +template +class basic_zstring_span +{ +public: + using value_type = CharT; + using const_value_type = std::add_const_t; + + using pointer = std::add_pointer_t; + using const_pointer = std::add_pointer_t; + + using zstring_type = basic_zstring; + using const_zstring_type = basic_zstring; + + using impl_type = span; + using string_span_type = basic_string_span; + + constexpr basic_zstring_span(impl_type s) noexcept : span_(s) + { + // expects a zero-terminated span + Expects(s[s.size() - 1] == '\0'); + } + + // copy + constexpr basic_zstring_span(const basic_zstring_span& other) = default; + +// move +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {} +#endif + + // assign + constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; + +// move assign +#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; +#else + constexpr basic_zstring_span& operator=(basic_zstring_span&& other) + { + span_ = std::move(other.span_); + return *this; + } +#endif + + constexpr bool empty() const noexcept { return span_.size() == 0; } + + constexpr string_span_type as_string_span() const noexcept + { + return span_.first(span_.size() - 1); + } + + constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } + + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } + +private: + impl_type span_; +}; + +template +using zstring_span = basic_zstring_span; + +template +using wzstring_span = basic_zstring_span; + +template +using czstring_span = basic_zstring_span; + +template +using cwzstring_span = basic_zstring_span; + +// operator == +template ::value || + std::is_convertible>>::value>> +bool operator==(const gsl::basic_string_span& one, const T& other) noexcept +{ + gsl::basic_string_span> tmp(other); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); +#else + return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); +#endif +} + +template ::value && + std::is_convertible>>::value>> +bool operator==(const T& one, const gsl::basic_string_span& other) noexcept +{ + gsl::basic_string_span> tmp(one); +#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL + return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); +#else + return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); +#endif +} + +// operator != +template , Extent>>::value>> +bool operator!=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one == other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator!=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one == other); +} + +// operator< +template , Extent>>::value>> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(gsl::basic_string_span one, const T& other) noexcept +{ + gsl::basic_string_span, Extent> tmp(other); + return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<(const T& one, gsl::basic_string_span other) noexcept +{ + gsl::basic_string_span, Extent> tmp(one); + return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); +} +#endif + +// operator <= +template , Extent>>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator<=(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(other < one); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator<=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(other < one); +} +#endif + +// operator> +template , Extent>>::value>> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(gsl::basic_string_span one, const T& other) noexcept +{ + return other < one; +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>(const T& one, gsl::basic_string_span other) noexcept +{ + return other < one; +} +#endif + +// operator >= +template , Extent>>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename Dummy = std::enable_if_t< + std::is_convertible, Extent>>::value && + !gsl::details::is_basic_string_span::value>> +bool operator>=(const T& one, gsl::basic_string_span 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_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(gsl::basic_string_span one, const T& other) noexcept +{ + return !(one < other); +} + +template < + typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, + typename DataType = typename T::value_type, + typename Dummy = std::enable_if_t< + !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && + std::is_convertible::value && + std::is_same().size(), *std::declval().data())>, + DataType>::value>> +bool operator>=(const T& one, gsl::basic_string_span other) noexcept +{ + return !(one < other); +} +#endif +} // namespace GSL + +#ifdef _MSC_VER + +#pragma warning(pop) + +#undef constexpr +#pragma pop_macro("constexpr") + +// VS 2013 workarounds +#if _MSC_VER <= 1800 + +#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 +#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE +#undef GSL_MSVC_NO_CPP14_STD_EQUAL +#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR + +#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 diff --git a/gsl/string_span.h b/gsl/string_span.h deleted file mode 100644 index e18e07a..0000000 --- a/gsl/string_span.h +++ /dev/null @@ -1,828 +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_SPAN_H -#define GSL_STRING_SPAN_H - -#include "gsl_assert.h" -#include "gsl_util.h" -#include "span.h" -#include -#include -#include - -#ifdef _MSC_VER - -// No MSVC does constexpr fully yet -#pragma push_macro("constexpr") -#define constexpr /* nothing */ - -#pragma warning(push) - -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#define GSL_MSVC_HAS_TYPE_DEDUCTION_BUG -#define GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE -#define GSL_MSVC_NO_CPP14_STD_EQUAL -#define GSL_MSVC_NO_DEFAULT_MOVE_CTOR - -// 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 -using basic_zstring = CharT*; - -template -using czstring = basic_zstring; - -template -using cwzstring = basic_zstring; - -template -using zstring = basic_zstring; - -template -using wzstring = basic_zstring; - -// -// 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 -span 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 -inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDIFF_MAX) -{ - return ensure_sentinel(sz, max); -} - -// TODO (neilmac) there is probably a better template-magic way to get the const and non-const -// overloads to share an implementation -inline span ensure_z(char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(const char* const& sz, std::ptrdiff_t max) -{ - auto len = strnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) -{ - auto len = wcsnlen(sz, narrow_cast(max)); - Ensures(sz[len] == 0); - return {sz, static_cast(len)}; -} - -template -span ensure_z(T (&sz)[N]) -{ - return ensure_z(&sz[0], static_cast(N)); -} - -template -span::type, dynamic_extent> -ensure_z(Cont& cont) -{ - return ensure_z(cont.data(), static_cast(cont.length())); -} - -template -class basic_string_span; - -namespace details -{ - template - struct is_basic_string_span_oracle : std::false_type - { - }; - - template - struct is_basic_string_span_oracle> : std::true_type - { - }; - - template - struct is_basic_string_span : is_basic_string_span_oracle> - { - }; - - template - struct length_func - { - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(strnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(strnlen(ptr, narrow_cast(length))); - } - }; - - template <> - struct length_func - { - std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept - { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); - } - }; -} - -// -// string_span and relatives -// -template -class basic_string_span -{ -public: - using element_type = CharT; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using const_reference = std::add_lvalue_reference_t>; - using impl_type = span; - - using index_type = typename impl_type::index_type; - 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() noexcept = default; - - // copy - constexpr basic_string_span(const basic_string_span& other) noexcept = default; - -// move -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span(basic_string_span&& other) noexcept = default; -#else - constexpr basic_string_span(basic_string_span&& other) : span_(std::move(other.span_)) {} -#endif - - // assign - constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; - -// move assign -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_string_span& operator=(basic_string_span&& other) noexcept = default; -#else - constexpr basic_string_span& operator=(basic_string_span&& other) noexcept - { - span_ = std::move(other.span_); - return *this; - } -#endif - - // from nullptr - constexpr basic_string_span(std::nullptr_t ptr) noexcept : span_(ptr) {} - - constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} - constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} - - // From static arrays - if 0-terminated, remove 0 from the view - // All other containers allow 0s within the length, so we do not remove them - template - constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) - { - } - - template > - constexpr basic_string_span(std::array& arr) noexcept : span_(arr) - { - } - - template > - constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) - { - } - - // Container signature should work for basic_string after C++17 version exists - template - constexpr basic_string_span(std::basic_string& str) - : span_(&str[0], str.length()) - { - } - - template - constexpr basic_string_span(const std::basic_string& str) - : span_(&str[0], str.length()) - { - } - - // from containers. Containers must have a pointer type and data() function signatures - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(Container& cont) : span_(cont) - { - } - - template ::value && - std::is_convertible::value && - std::is_convertible().data())>::value>> - constexpr basic_string_span(const Container& cont) : span_(cont) - { - } - - // from string_span - template < - class OtherValueType, std::ptrdiff_t OtherExtent, - class = std::enable_if_t::impl_type, impl_type>::value>> - constexpr basic_string_span(basic_string_span other) - : span_(other.data(), other.length()) - { - } - - template - constexpr basic_string_span first() const - { - return {span_.template first()}; - } - - constexpr basic_string_span first(index_type count) const - { - return {span_.first(count)}; - } - - template - constexpr basic_string_span last() const - { - return {span_.template last()}; - } - - constexpr basic_string_span last(index_type count) const - { - return {span_.last(count)}; - } - - template - constexpr basic_string_span subspan() const - { - return {span_.template subspan()}; - } - - constexpr basic_string_span - subspan(index_type offset, index_type count = dynamic_extent) const - { - return {span_.subspan(offset, count)}; - } - - constexpr reference operator[](index_type idx) const { return span_[idx]; } - constexpr reference operator()(index_type idx) const { return span_[idx]; } - - constexpr pointer data() const { return span_.data(); } - - constexpr index_type length() const noexcept { return span_.size(); } - constexpr index_type size() const noexcept { return span_.size(); } - constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } - constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } - constexpr bool empty() const noexcept { return size() == 0; } - - 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 { return 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) - { - return {sz, details::length_func()(sz, max)}; - } - - template - static impl_type remove_z(element_type (&sz)[N]) - { - return remove_z(&sz[0], narrow_cast(N)); - } - - impl_type span_; -}; - -template -using string_span = basic_string_span; - -template -using cstring_span = basic_string_span; - -template -using wstring_span = basic_string_span; - -template -using cwstring_span = basic_string_span; - -// -// to_string() allow (explicit) conversions from string_span to string -// -#ifndef GSL_MSVC_HAS_TYPE_DEDUCTION_BUG - -template -std::basic_string::type> -to_string(basic_string_span view) -{ - return {view.data(), static_cast(view.length())}; -} - -#else - -inline std::string to_string(cstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::string to_string(string_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::wstring to_string(cwstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -inline std::wstring to_string(wstring_span<> view) -{ - return {view.data(), static_cast(view.length())}; -} - -#endif - -// zero-terminated string span, used to convert -// zero-terminated spans to legacy strings -template -class basic_zstring_span -{ -public: - using value_type = CharT; - using const_value_type = std::add_const_t; - - using pointer = std::add_pointer_t; - using const_pointer = std::add_pointer_t; - - using zstring_type = basic_zstring; - using const_zstring_type = basic_zstring; - - using impl_type = span; - using string_span_type = basic_string_span; - - constexpr basic_zstring_span(impl_type s) noexcept : span_(s) - { - // expects a zero-terminated span - Expects(s[s.size() - 1] == '\0'); - } - - // copy - constexpr basic_zstring_span(const basic_zstring_span& other) = default; - -// move -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_zstring_span(basic_zstring_span&& other) = default; -#else - constexpr basic_zstring_span(basic_zstring_span&& other) : span_(std::move(other.span_)) {} -#endif - - // assign - constexpr basic_zstring_span& operator=(const basic_zstring_span& other) = default; - -// move assign -#ifndef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; -#else - constexpr basic_zstring_span& operator=(basic_zstring_span&& other) - { - span_ = std::move(other.span_); - return *this; - } -#endif - - constexpr bool empty() const noexcept { return span_.size() == 0; } - - constexpr string_span_type as_string_span() const noexcept - { - return span_.first(span_.size() - 1); - } - - constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } - - constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } - -private: - impl_type span_; -}; - -template -using zstring_span = basic_zstring_span; - -template -using wzstring_span = basic_zstring_span; - -template -using czstring_span = basic_zstring_span; - -template -using cwzstring_span = basic_zstring_span; - -// operator == -template ::value || - std::is_convertible>>::value>> -bool operator==(const gsl::basic_string_span& one, const T& other) noexcept -{ - gsl::basic_string_span> tmp(other); -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (one.size() == tmp.size()) && std::equal(one.begin(), one.end(), tmp.begin()); -#else - return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); -#endif -} - -template ::value && - std::is_convertible>>::value>> -bool operator==(const T& one, const gsl::basic_string_span& other) noexcept -{ - gsl::basic_string_span> tmp(one); -#ifdef GSL_MSVC_NO_CPP14_STD_EQUAL - return (tmp.size() == other.size()) && std::equal(tmp.begin(), tmp.end(), other.begin()); -#else - return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); -#endif -} - -// operator != -template , Extent>>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one == other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator!=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one == other); -} - -// operator< -template , Extent>>::value>> -bool operator<(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(gsl::basic_string_span one, const T& other) noexcept -{ - gsl::basic_string_span, Extent> tmp(other); - return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<(const T& one, gsl::basic_string_span other) noexcept -{ - gsl::basic_string_span, Extent> tmp(one); - return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); -} -#endif - -// operator <= -template , Extent>>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator<=(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(other < one); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator<=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(other < one); -} -#endif - -// operator> -template , Extent>>::value>> -bool operator>(gsl::basic_string_span one, const T& other) noexcept -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(gsl::basic_string_span one, const T& other) noexcept -{ - return other < one; -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>(const T& one, gsl::basic_string_span other) noexcept -{ - return other < one; -} -#endif - -// operator >= -template , Extent>>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename Dummy = std::enable_if_t< - std::is_convertible, Extent>>::value && - !gsl::details::is_basic_string_span::value>> -bool operator>=(const T& one, gsl::basic_string_span 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_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) noexcept -{ - return !(one < other); -} - -template < - typename CharT, std::ptrdiff_t Extent = gsl::dynamic_extent, typename T, - typename DataType = typename T::value_type, - typename Dummy = std::enable_if_t< - !gsl::details::is_span::value && !gsl::details::is_basic_string_span::value && - std::is_convertible::value && - std::is_same().size(), *std::declval().data())>, - DataType>::value>> -bool operator>=(const T& one, gsl::basic_string_span other) noexcept -{ - return !(one < other); -} -#endif -} // namespace GSL - -#ifdef _MSC_VER - -#pragma warning(pop) - -#undef constexpr -#pragma pop_macro("constexpr") - -// VS 2013 workarounds -#if _MSC_VER <= 1800 - -#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 -#undef GSL_MSVC_HAS_SFINAE_SUBSTITUTION_ICE -#undef GSL_MSVC_NO_CPP14_STD_EQUAL -#undef GSL_MSVC_NO_DEFAULT_MOVE_CTOR - -#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 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a59470..3c3125e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,7 +33,7 @@ else() endif() function(add_gsl_test name) - add_executable(${name} ${name}.cpp ../gsl/gsl.h ../gsl/gsl_assert.h ../gsl/gsl_util.h ../gsl/multi_span.h ../gsl/span.h ../gsl/string_span.h) + add_executable(${name} ${name}.cpp ../gsl/gsl ../gsl/gsl_assert ../gsl/gsl_util ../gsl/multi_span ../gsl/span ../gsl/string_span) target_link_libraries(${name} UnitTest++) install(TARGETS ${name} RUNTIME DESTINATION bin diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp index 4ac1856..a251200 100644 --- a/tests/assertion_tests.cpp +++ b/tests/assertion_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include using namespace gsl; diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp index 53d93d1..008fddf 100644 --- a/tests/at_tests.cpp +++ b/tests/at_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp index 7d5a071..d10bf6d 100644 --- a/tests/bounds_tests.cpp +++ b/tests/bounds_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace std; diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 183bebf..f2f8026 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index b2e5b5f..f04ba84 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index bc12e9b..526b074 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index 3c82022..6680981 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index fb20f77..8479aa1 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/strided_span_tests.cpp b/tests/strided_span_tests.cpp index 4c07670..86666d1 100644 --- a/tests/strided_span_tests.cpp +++ b/tests/strided_span_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include #include diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 3eeacb0..7e623f3 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 8c98223..9f4ba02 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -15,7 +15,7 @@ /////////////////////////////////////////////////////////////////////////////// #include -#include +#include #include using namespace gsl; -- cgit v1.2.3 From d6ac64027194678bba8f7b518d729075bac27a2d Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 10 Aug 2016 19:32:00 -0700 Subject: Added basic test for interop with std::regex as per Issue #271. --- tests/span_tests.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 8479aa1..2243160 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace std; using namespace gsl; @@ -1243,6 +1244,29 @@ SUITE(span_tests) }; CHECK_THROW(f(), fail_fast); } + + TEST(interop_with_std_regex) + { + char lat[] = { '1', '2', '3', '4', '5', '6', 'E', 'F', 'G' }; + span s = lat; + auto f_it = s.begin() + 7; + + std::match_results::iterator> match; + + std::regex_match(s.begin(), s.end(), match, std::regex(".*")); + CHECK(match.ready()); + CHECK(!match.empty()); + CHECK(match[0].matched); + CHECK(match[0].first == s.begin()); + CHECK(match[0].second == s.end()); + + std::regex_search(s.begin(), s.end(), match, std::regex("F")); + CHECK(match.ready()); + CHECK(!match.empty()); + CHECK(match[0].matched); + CHECK(match[0].first == f_it); + CHECK(match[0].second == (f_it + 1)); + } } int main(int, const char* []) { return UnitTest::RunAllTests(); } -- cgit v1.2.3 From 582ae8c87f938bee43222596d0561eb578c3d82c Mon Sep 17 00:00:00 2001 From: Vincent Isambart Date: Sat, 13 Aug 2016 10:15:59 +0900 Subject: Fix #323 - Badly positioned noexcept --- gsl/span | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsl/span b/gsl/span index 3b8318d..1dc4600 100644 --- a/gsl/span +++ b/gsl/span @@ -179,7 +179,7 @@ namespace details using pointer = std::add_pointer_t; using reference = std::add_lvalue_reference_t; - constexpr span_iterator() : span_iterator(nullptr, 0) noexcept {} + constexpr span_iterator() noexcept : span_iterator(nullptr, 0) {} constexpr span_iterator(const Span* span, typename Span::index_type index) : span_(span), index_(index) -- cgit v1.2.3 From 36d56ebcb3dd6dcd2ce4ae3825485a95ae3146b6 Mon Sep 17 00:00:00 2001 From: Vincent Isambart Date: Sat, 13 Aug 2016 10:16:59 +0900 Subject: Add missing newline at end of file --- gsl/gsl_byte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 5a9c327..cb096e7 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -122,4 +122,4 @@ constexpr IntegerType to_integer(byte b) noexcept #endif // _MSC_VER -#endif // GSL_BYTE_H \ No newline at end of file +#endif // GSL_BYTE_H -- cgit v1.2.3 From 2d4b552c82a5d4dab19590f086c892f91adf7b03 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sun, 14 Aug 2016 13:20:32 +0800 Subject: Fix inconsistent naming --- CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 990b8e1..10e6c32 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -## Contributing to the Guidelines Support Library +## Contributing to the Guideline Support Library -The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +The Guideline Support Library (GSL) contains functions and types that are suggested for use by the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines. GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue diff --git a/README.md b/README.md index 58bb46c..9673df5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) +# GSL: Guideline Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) -The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +The Guideline Support Library (GSL) contains functions and types that are suggested for use by the [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. -- cgit v1.2.3 From aadcce275eb81a9de41354815330a0f815475740 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Thu, 18 Aug 2016 20:32:08 -0600 Subject: Fix issue: Provide platform support for strnlen Not all platforms have support for strnlen (e.g. cygwin) as this is a non-standard function. This patch provides a custom implementation of strnlen for these platforms. --- gsl/string_span | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/gsl/string_span b/gsl/string_span index fa3d2ff..415744b 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -56,6 +56,10 @@ #endif // _MSC_VER <= 1800 #endif // _MSC_VER +#ifndef __CYGWIN__ +#define GSL_PLATFORM_HAS_STRNLEN +#endif + // In order to test the library, we need it to throw exceptions that we can catch #ifdef GSL_THROW_ON_CONTRACT_VIOLATION @@ -95,6 +99,27 @@ using zstring = basic_zstring; template using wzstring = basic_zstring; +namespace details +{ + inline std::size_t string_length(const char *str, std::size_t n) + { +#ifdef GSL_PLATFORM_HAS_STRNLEN + return strnlen(str, n); +#else + if (str == nullptr || n == 0) + return 0; + + std::size_t len = 0; + span str_span{str, n}; + + while (len < n && str_span[len]) + len++; + + return len; +#endif + } +} + // // ensure_sentinel() // @@ -127,14 +152,14 @@ inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDI // overloads to share an implementation inline span ensure_z(char* const& sz, std::ptrdiff_t max) { - auto len = strnlen(sz, narrow_cast(max)); + auto len = details::string_length(sz, narrow_cast(max)); Ensures(sz[len] == 0); return {sz, static_cast(len)}; } inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { - auto len = strnlen(sz, narrow_cast(max)); + auto len = details::string_length(sz, narrow_cast(max)); Ensures(sz[len] == 0); return {sz, static_cast(len)}; } @@ -196,7 +221,7 @@ namespace details { std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(strnlen(ptr, narrow_cast(length))); + return narrow_cast(details::string_length(ptr, narrow_cast(length))); } }; @@ -214,7 +239,7 @@ namespace details { std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(strnlen(ptr, narrow_cast(length))); + return narrow_cast(details::string_length(ptr, narrow_cast(length))); } }; -- cgit v1.2.3 From 134f2db5d9c9039b04cb2da1513c7ffdd7906fb8 Mon Sep 17 00:00:00 2001 From: ericLemanissier Date: Tue, 23 Aug 2016 10:30:06 +0200 Subject: Specialize gsl::at for span span being a view and not a container, the generic version of gsl::at is not valid any more for span. This commits adds a specialization of gsl::at for span --- gsl/span | 8 ++++++++ tests/span_tests.cpp | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/gsl/span b/gsl/span index 1dc4600..7ba3f3d 100644 --- a/gsl/span +++ b/gsl/span @@ -653,6 +653,14 @@ as_writeable_bytes(span s) noexcept return {reinterpret_cast(s.data()), s.size_bytes()}; } +// Specialization of gsl::at for span +template +constexpr ElementType& at(const span& s, size_t index) +{ + // No bounds checking here because it is done in span::operator[] called below + return s[index]; +} + } // namespace gsl #ifdef _MSC_VER diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 712492f..571a821 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -1354,6 +1354,13 @@ SUITE(span_tests) CHECK(match[0].first == f_it); CHECK(match[0].second == (f_it + 1)); } + + TEST(interop_with_gsl_at) + { + int arr[5] = {1, 2, 3, 4, 5}; + span s{arr}; + CHECK(at(s,0) == 1 && at(s,1) == 2); + } } int main(int, const char* []) { return UnitTest::RunAllTests(); } -- cgit v1.2.3 From a8c5004b7060aac82405bd5f843ec40731746a0b Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 24 Aug 2016 10:01:42 -0700 Subject: Update CMakeLists.txt --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb5911c..2f1b0f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ project(GSL CXX) set(GSL_HEADERS "gsl/gsl" "gsl/gsl_assert" + "gsl/gsl_byte" + "gsl/gsl_util" + "gsl/multi_span" "gsl/span" "gsl/string_span" ) -- cgit v1.2.3 From 3aea9f7201c4bcb03011b47ce296f3b4afaea7ff Mon Sep 17 00:00:00 2001 From: Martin Moene Date: Wed, 24 Aug 2016 19:53:53 +0200 Subject: Define constexpr as /*constexpr*/ when unavailable (also noexcept) --- gsl/gsl | 4 ++-- gsl/gsl_byte | 4 ++-- gsl/gsl_util | 4 ++-- gsl/multi_span | 6 +++--- gsl/span | 6 +++--- gsl/string_span | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/gsl/gsl b/gsl/gsl index 656ebe0..12a9676 100644 --- a/gsl/gsl +++ b/gsl/gsl @@ -30,13 +30,13 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr /*constexpr*/ // MSVC 2013 workarounds #if _MSC_VER <= 1800 // noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept /*noexcept*/ // turn off some misguided warnings #pragma warning(push) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index cb096e7..91ce9d2 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -26,11 +26,11 @@ // constexpr is not understood #pragma push_macro("constexpr") -#define constexpr +#define constexpr /*constexpr*/ // noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept /*noexcept*/ #endif // _MSC_VER <= 1800 diff --git a/gsl/gsl_util b/gsl/gsl_util index 4a0dabe..17a1edb 100644 --- a/gsl/gsl_util +++ b/gsl/gsl_util @@ -29,7 +29,7 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr /*constexpr*/ #pragma warning(push) #pragma warning(disable : 4127) // conditional expression is constant @@ -38,7 +38,7 @@ #if _MSC_VER <= 1800 // noexcept is not understood #pragma push_macro("noexcept") -#define noexcept +#define noexcept /*noexcept*/ // turn off some misguided warnings #pragma warning(push) diff --git a/gsl/multi_span b/gsl/multi_span index 5ddf6fa..0abc7c1 100644 --- a/gsl/multi_span +++ b/gsl/multi_span @@ -44,7 +44,7 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr /*constexpr*/ // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -55,7 +55,7 @@ // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif // turn off some misguided warnings @@ -73,7 +73,7 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif // GSL_THROW_ON_CONTRACT_VIOLATION diff --git a/gsl/span b/gsl/span index 1dc4600..3044cfd 100644 --- a/gsl/span +++ b/gsl/span @@ -44,7 +44,7 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr +#define constexpr /*constexpr*/ // VS 2013 workarounds #if _MSC_VER <= 1800 @@ -56,7 +56,7 @@ // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif #pragma push_macro("alignof") @@ -77,7 +77,7 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif // GSL_THROW_ON_CONTRACT_VIOLATION diff --git a/gsl/string_span b/gsl/string_span index 415744b..8e60183 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -30,7 +30,7 @@ // No MSVC does constexpr fully yet #pragma push_macro("constexpr") -#define constexpr /* nothing */ +#define constexpr /*constexpr*/ #pragma warning(push) @@ -50,7 +50,7 @@ // noexcept is not understood #ifndef GSL_THROW_ON_CONTRACT_VIOLATION #pragma push_macro("noexcept") -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif #endif // _MSC_VER <= 1800 @@ -67,7 +67,7 @@ #pragma push_macro("noexcept") #endif -#define noexcept /* nothing */ +#define noexcept /*noexcept*/ #endif // GSL_THROW_ON_CONTRACT_VIOLATION -- cgit v1.2.3 From 95bbaa1ec2c2fa39ecea4dc3c685f771551c59ff Mon Sep 17 00:00:00 2001 From: Kris Date: Sun, 28 Aug 2016 21:55:58 +0100 Subject: Added to_byte method for issue #329 I have added the to_byte method and updated the unit tests. This gives slightly nicer syntax than static_cast, is better than the c-style cast used in the unit test. See: https://github.com/Microsoft/GSL/issues/329#issuecomment-240588515 --- gsl/gsl_byte | 6 ++++++ tests/byte_tests.cpp | 41 +++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 91ce9d2..98749a7 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -106,6 +106,12 @@ constexpr IntegerType to_integer(byte b) noexcept return {b}; } +constexpr byte to_byte(unsigned char i) noexcept +{ + return static_cast(i); +} + + } // namespace gsl #ifdef _MSC_VER diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index f2f8026..d6d4823 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -43,6 +43,11 @@ SUITE(byte_tests) byte b = byte(12); CHECK(static_cast(b) == 12); } + + { + byte b = to_byte(12); + CHECK(static_cast(b) == 12); + } // waiting for C++17 enum class direct initializer support //{ @@ -53,38 +58,38 @@ SUITE(byte_tests) TEST(bitwise_operations) { - byte b = byte(0xFF); + byte b = to_byte(0xFF); - byte a = byte(0x00); - CHECK((b | a) == byte(0xFF)); - CHECK(a == byte(0x00)); + byte a = to_byte(0x00); + CHECK((b | a) == to_byte(0xFF)); + CHECK(a == to_byte(0x00)); a |= b; - CHECK(a == byte(0xFF)); + CHECK(a == to_byte(0xFF)); - a = byte(0x01); - CHECK((b & a) == byte(0x01)); + a = to_byte(0x01); + CHECK((b & a) == to_byte(0x01)); a &= b; - CHECK(a == byte(0x01)); + CHECK(a == to_byte(0x01)); - CHECK((b ^ a) == byte(0xFE)); + CHECK((b ^ a) == to_byte(0xFE)); - CHECK(a == byte(0x01)); + CHECK(a == to_byte(0x01)); a ^= b; - CHECK(a == byte(0xFE)); + CHECK(a == to_byte(0xFE)); - a = byte(0x01); - CHECK(~a == byte(0xFE)); + a = to_byte(0x01); + CHECK(~a == to_byte(0xFE)); - a = byte(0xFF); - CHECK((a << 4) == byte(0xF0)); - CHECK((a >> 4) == byte(0x0F)); + a = to_byte(0xFF); + CHECK((a << 4) == to_byte(0xF0)); + CHECK((a >> 4) == to_byte(0x0F)); a <<= 4; - CHECK(a == byte(0xF0)); + CHECK(a == to_byte(0xF0)); a >>= 4; - CHECK(a == byte(0x0F)); + CHECK(a == to_byte(0x0F)); } } -- cgit v1.2.3 From ec109d2374a6fe949b9760c5f55869119072f083 Mon Sep 17 00:00:00 2001 From: Gary Furnish Date: Sun, 4 Sep 2016 00:29:10 -0600 Subject: Add to_basic_string --- gsl/string_span | 10 ++++++++++ tests/string_span_tests.cpp | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/gsl/string_span b/gsl/string_span index 8e60183..cd49998 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -486,6 +486,16 @@ inline std::wstring to_string(wstring_span<> view) #endif +template , + typename Allocator = std::allocator, + typename gCharT, + std::ptrdiff_t Extent> +std::basic_string to_basic_string(basic_string_span view) +{ + return {view.data(), static_cast(view.length())}; +} + // zero-terminated string span, used to convert // zero-terminated spans to legacy strings template diff --git a/tests/string_span_tests.cpp b/tests/string_span_tests.cpp index 7e623f3..a1cfe79 100644 --- a/tests/string_span_tests.cpp +++ b/tests/string_span_tests.cpp @@ -114,6 +114,18 @@ SUITE(string_span_tests) CHECK(s2.length() == 5); } + TEST(TestToBasicString) + { + auto s = gsl::to_basic_string,::std::allocator>(cstring_span<>{}); + CHECK(s.length() == 0); + + char stack_string[] = "Hello"; + cstring_span<> v = ensure_z(stack_string); + auto s2 = gsl::to_basic_string,::std::allocator>(v); + CHECK(static_cast::index_type>(s2.length()) == v.length()); + CHECK(s2.length() == 5); + } + TEST(EqualityAndImplicitConstructors) { { -- cgit v1.2.3 From 9e0807f1b1f1d383d67a6a23afbe69b6ab594b20 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 6 Sep 2016 18:45:53 -0700 Subject: Fix typo in operator | for gsl::byte. --- gsl/gsl_byte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 91ce9d2..5134032 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -75,7 +75,7 @@ constexpr byte& operator|=(byte& l, byte r) noexcept constexpr byte operator|(byte l, byte r) noexcept { - return byte(static_cast(l) + static_cast(r)); + return byte(static_cast(l) | static_cast(r)); } constexpr byte& operator&=(byte& l, byte r) noexcept -- cgit v1.2.3 From 55b232dde4189525fe211d0e7c8f952a3e3e061d Mon Sep 17 00:00:00 2001 From: Kris Cruickshank Date: Wed, 7 Sep 2016 21:38:43 +0100 Subject: Issue: #329 This is my best attempt at fixing the issues raised by @gdr-at-ms while supporting VS2013, VS2015, providing shorter syntax than the static_cast and giving developer meaningful errors when they do the wrong thing. --- gsl/gsl_byte | 26 ++++++++++++++++++++++++-- tests/byte_tests.cpp | 43 ++++++++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 98749a7..c973320 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -106,11 +106,33 @@ constexpr IntegerType to_integer(byte b) noexcept return {b}; } -constexpr byte to_byte(unsigned char i) noexcept +template +constexpr byte to_byte_impl(T t) noexcept { - return static_cast(i); + static_assert( + false, + "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " + "If you are calling to_byte with an integer contant use: gsl::to_byte() version." + ) +} +template<> +constexpr byte to_byte_impl(unsigned char t) noexcept +{ + return byte(t); } +template +constexpr byte to_byte(T t) noexcept +{ + return to_byte_impl::value, T>(t); +} + +template +constexpr byte to_byte() noexcept +{ + static_assert(I >= 0 && I <= 255, "gsl::byte only has 8 bits of storage, values must be in range 0-255"); + return static_cast(I); +} } // namespace gsl diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index d6d4823..59ff0cd 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -45,7 +45,12 @@ SUITE(byte_tests) } { - byte b = to_byte(12); + byte b = to_byte<12>(); + CHECK(static_cast(b) == 12); + } + { + unsigned char uc = 12; + byte b = to_byte(uc); CHECK(static_cast(b) == 12); } @@ -58,38 +63,38 @@ SUITE(byte_tests) TEST(bitwise_operations) { - byte b = to_byte(0xFF); + byte b = to_byte<0xFF>(); - byte a = to_byte(0x00); - CHECK((b | a) == to_byte(0xFF)); - CHECK(a == to_byte(0x00)); + byte a = to_byte<0x00>(); + CHECK((b | a) == to_byte<0xFF>()); + CHECK(a == to_byte<0x00>()); a |= b; - CHECK(a == to_byte(0xFF)); + CHECK(a == to_byte<0xFF>()); - a = to_byte(0x01); - CHECK((b & a) == to_byte(0x01)); + a = to_byte<0x01>(); + CHECK((b & a) == to_byte<0x01>()); a &= b; - CHECK(a == to_byte(0x01)); + CHECK(a == to_byte<0x01>()); - CHECK((b ^ a) == to_byte(0xFE)); + CHECK((b ^ a) == to_byte<0xFE>()); - CHECK(a == to_byte(0x01)); + CHECK(a == to_byte<0x01>()); a ^= b; - CHECK(a == to_byte(0xFE)); + CHECK(a == to_byte<0xFE>()); - a = to_byte(0x01); - CHECK(~a == to_byte(0xFE)); + a = to_byte<0x01>(); + CHECK(~a == to_byte<0xFE>()); - a = to_byte(0xFF); - CHECK((a << 4) == to_byte(0xF0)); - CHECK((a >> 4) == to_byte(0x0F)); + a = to_byte<0xFF>(); + CHECK((a << 4) == to_byte<0xF0>()); + CHECK((a >> 4) == to_byte<0x0F>()); a <<= 4; - CHECK(a == to_byte(0xF0)); + CHECK(a == to_byte<0xF0>()); a >>= 4; - CHECK(a == to_byte(0x0F)); + CHECK(a == to_byte<0x0F>()); } } -- cgit v1.2.3 From 7d083fd08cb5fbf1e5e953adf72c2c38c258c888 Mon Sep 17 00:00:00 2001 From: Kris Date: Wed, 7 Sep 2016 22:16:10 +0100 Subject: Issue #329, fixed build issues on clang Fixed build issues that clang shows up the MS VS accepts. --- gsl/gsl_byte | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index c973320..f502ab0 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -106,14 +106,15 @@ constexpr IntegerType to_integer(byte b) noexcept return {b}; } -template +template constexpr byte to_byte_impl(T t) noexcept { static_assert( - false, + E, "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " "If you are calling to_byte with an integer contant use: gsl::to_byte() version." - ) + ); + return static_cast(t); } template<> constexpr byte to_byte_impl(unsigned char t) noexcept -- cgit v1.2.3 From 3b2419532e8cdf78a4ba585f4dd15b9e08a4a48f Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 12 Sep 2016 18:51:23 -0700 Subject: Corrected SFINAE for conversion constructors on span. --- gsl/span | 31 +------------------------------ tests/span_tests.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/gsl/span b/gsl/span index 8803ef1..cce8253 100644 --- a/gsl/span +++ b/gsl/span @@ -123,23 +123,6 @@ namespace details { }; - template - struct is_allowed_pointer_conversion - : public std::integral_constant::value && - std::is_pointer::value && - std::is_convertible::value> - { - }; - - template - struct is_allowed_integral_conversion - : public std::integral_constant< - bool, std::is_integral::value && std::is_integral::value && - sizeof(From) == sizeof(To) && alignof(From) == alignof(To) && - std::is_convertible::value> - { - }; - template struct is_allowed_extent_conversion : public std::integral_constant struct is_allowed_element_type_conversion : public std::integral_constant>::value || - is_allowed_pointer_conversion::value || - is_allowed_integral_conversion::value> - { - }; - - template - struct is_allowed_element_type_conversion - : public std::integral_constant::value> - { - }; - - template - struct is_allowed_element_type_conversion : public std::true_type + std::is_convertible::value> { }; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 571a821..a1dd64d 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -423,6 +423,7 @@ SUITE(span_tests) span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } + #ifdef CONFIRM_COMPILATION_ERRORS { span s{arr}; @@ -437,6 +438,7 @@ SUITE(span_tests) { span s{arr}; } +#endif { auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; @@ -444,7 +446,6 @@ SUITE(span_tests) // try to take a temporary std::array take_a_span(get_an_array()); } -#endif } TEST(from_std_array_const_constructor) @@ -460,6 +461,7 @@ SUITE(span_tests) span s{arr}; CHECK(s.size() == narrow_cast(arr.size()) && s.data() == arr.data()); } + #ifdef CONFIRM_COMPILATION_ERRORS { span s{arr}; @@ -509,7 +511,7 @@ SUITE(span_tests) { #ifdef CONFIRM_COMPILATION_ERRORS span s{cstr}; -#endif +#endif span cs{cstr}; CHECK(cs.size() == narrow_cast(cstr.size()) && cs.data() == cstr.data()); @@ -520,7 +522,7 @@ SUITE(span_tests) auto get_temp_vector = []() -> std::vector { return {}; }; auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_vector()); -#endif +#endif } { @@ -534,7 +536,7 @@ SUITE(span_tests) auto get_temp_string = []() -> std::string { return{}; }; auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_string()); -#endif +#endif } { @@ -552,11 +554,9 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS auto get_temp_string = []() -> const std::string { return {}; }; auto use_span = [](span s) { static_cast(s); }; use_span(get_temp_string()); -#endif } { @@ -583,6 +583,7 @@ SUITE(span_tests) #endif } +#ifdef CONFIRM_COMPILATION_ERRORS { span s; span s2 = s; @@ -596,12 +597,11 @@ SUITE(span_tests) } { -#ifdef CONFIRM_COMPILATION_ERRORS span s; span s2 = s; static_cast(s2); -#endif } +#endif } TEST(copy_move_and_assignment) @@ -689,7 +689,7 @@ SUITE(span_tests) span av = arr; #ifdef CONFIRM_COMPILATION_ERRORS CHECK(av.last<6>().length() == 6); -#endif +#endif CHECK_THROW(av.last(6).length(), fail_fast); } -- cgit v1.2.3 From bc70a93bba6c6f63486df55295145d043604a228 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Mon, 12 Sep 2016 19:06:45 -0700 Subject: Removed unnecessary reinterpret_cast<> from span implementation. --- gsl/span | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gsl/span b/gsl/span index cce8253..d2e16dc 100644 --- a/gsl/span +++ b/gsl/span @@ -420,7 +420,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) - : storage_(reinterpret_cast(other.data()), + : storage_(static_cast(other.data()), details::extent_type(other.size())) { } @@ -431,7 +431,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) - : storage_(reinterpret_cast(other.data()), + : storage_(static_cast(other.data()), details::extent_type(other.size())) { } -- cgit v1.2.3 From ad5275ca7847b8f32c719dd0bc3e9b55ed5973c2 Mon Sep 17 00:00:00 2001 From: Steve Brain Date: Tue, 13 Sep 2016 16:07:34 +1000 Subject: Added inline to fix compilation warnings on msvc 2013 --- gsl/gsl_byte | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 5134032..8f57f67 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -26,7 +26,7 @@ // constexpr is not understood #pragma push_macro("constexpr") -#define constexpr /*constexpr*/ +#define constexpr /*constexpr*/ // noexcept is not understood #pragma push_macro("noexcept") @@ -68,37 +68,37 @@ constexpr byte operator>>(byte b, IntegerType shift) noexcept return byte(static_cast(b) >> shift); } -constexpr byte& operator|=(byte& l, byte r) noexcept +inline constexpr byte& operator|=(byte& l, byte r) noexcept { return l = byte(static_cast(l) | static_cast(r)); } -constexpr byte operator|(byte l, byte r) noexcept +inline constexpr byte operator|(byte l, byte r) noexcept { return byte(static_cast(l) | static_cast(r)); } -constexpr byte& operator&=(byte& l, byte r) noexcept +inline constexpr byte& operator&=(byte& l, byte r) noexcept { return l = byte(static_cast(l) & static_cast(r)); } -constexpr byte operator&(byte l, byte r) noexcept +inline constexpr byte operator&(byte l, byte r) noexcept { return byte(static_cast(l) & static_cast(r)); } -constexpr byte& operator^=(byte& l, byte r) noexcept +inline constexpr byte& operator^=(byte& l, byte r) noexcept { return l = byte(static_cast(l) ^ static_cast(r)); } -constexpr byte operator^(byte l, byte r) noexcept +inline constexpr byte operator^(byte l, byte r) noexcept { return byte(static_cast(l) ^ static_cast(r)); } -constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } +inline constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } template ::value>> constexpr IntegerType to_integer(byte b) noexcept -- cgit v1.2.3 From edceed8075e14c86aac7f86347331aafac122c30 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Tue, 13 Sep 2016 14:30:02 -0700 Subject: Adding inline to all byte-related functions for MSVC 2013. --- gsl/gsl_byte | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index bee1092..ab5e4e3 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -26,7 +26,7 @@ // constexpr is not understood #pragma push_macro("constexpr") -#define constexpr /*constexpr*/ +#define constexpr /*constexpr*/ // noexcept is not understood #pragma push_macro("noexcept") @@ -45,25 +45,25 @@ enum class byte : unsigned char }; template ::value>> -constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +inline constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept { return b = byte(static_cast(b) << shift); } template ::value>> -constexpr byte operator<<(byte b, IntegerType shift) noexcept +inline constexpr byte operator<<(byte b, IntegerType shift) noexcept { return byte(static_cast(b) << shift); } template ::value>> -constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +inline constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept { return b = byte(static_cast(b) >> shift); } template ::value>> -constexpr byte operator>>(byte b, IntegerType shift) noexcept +inline constexpr byte operator>>(byte b, IntegerType shift) noexcept { return byte(static_cast(b) >> shift); } @@ -101,13 +101,13 @@ inline constexpr byte operator^(byte l, byte r) noexcept inline constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } template ::value>> -constexpr IntegerType to_integer(byte b) noexcept +inline constexpr IntegerType to_integer(byte b) noexcept { return {b}; } template -constexpr byte to_byte_impl(T t) noexcept +inline constexpr byte to_byte_impl(T t) noexcept { static_assert( E, @@ -117,19 +117,19 @@ constexpr byte to_byte_impl(T t) noexcept return static_cast(t); } template<> -constexpr byte to_byte_impl(unsigned char t) noexcept +inline constexpr byte to_byte_impl(unsigned char t) noexcept { return byte(t); } template -constexpr byte to_byte(T t) noexcept +inline constexpr byte to_byte(T t) noexcept { return to_byte_impl::value, T>(t); } template -constexpr byte to_byte() noexcept +inline constexpr byte to_byte() noexcept { static_assert(I >= 0 && I <= 255, "gsl::byte only has 8 bits of storage, values must be in range 0-255"); return static_cast(I); -- cgit v1.2.3 From 831be5de39fd297aa8b434554321b54a438107d3 Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Wed, 14 Sep 2016 22:01:02 -0700 Subject: Removed redundant static_cast<> and is_same<> test. --- gsl/span | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/gsl/span b/gsl/span index d2e16dc..a9a3322 100644 --- a/gsl/span +++ b/gsl/span @@ -132,8 +132,7 @@ namespace details template struct is_allowed_element_type_conversion - : public std::integral_constant>::value || - std::is_convertible::value> + : public std::integral_constant::value> { }; @@ -420,8 +419,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) - : storage_(static_cast(other.data()), - details::extent_type(other.size())) + : storage_(other.data(), details::extent_type(other.size())) { } @@ -431,8 +429,7 @@ public: details::is_allowed_extent_conversion::value && details::is_allowed_element_type_conversion::value>> constexpr span(span&& other) - : storage_(static_cast(other.data()), - details::extent_type(other.size())) + : storage_(other.data(), details::extent_type(other.size())) { } @@ -626,7 +623,7 @@ as_writeable_bytes(span s) noexcept // Specialization of gsl::at for span template -constexpr ElementType& at(const span& s, size_t index) +constexpr ElementType& at(const span& s, size_t index) { // No bounds checking here because it is done in span::operator[] called below return s[index]; -- cgit v1.2.3 From ad991370daccc087f4dca8c239df413c45e5ec2f Mon Sep 17 00:00:00 2001 From: Martin Moene Date: Sat, 17 Sep 2016 09:16:15 +0200 Subject: Add tests for to_integer(): they fail --- tests/byte_tests.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 59ff0cd..3bbf382 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -96,6 +96,24 @@ SUITE(byte_tests) a >>= 4; CHECK(a == to_byte<0x0F>()); } + + TEST(to_integer) + { + byte b = to_byte<0x12>(); + + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + CHECK(0x12 == gsl::to_integer(b)); + +// CHECK(0x12 == gsl::to_integer(b)); // expect compile-time error +// CHECK(0x12 == gsl::to_integer(b)); // expect compile-time error + } } } -- cgit v1.2.3 From 6cf154e067550440e9580cad0744afd12e1ccafd Mon Sep 17 00:00:00 2001 From: Martin Moene Date: Sat, 17 Sep 2016 09:24:34 +0200 Subject: Fix to_integer() --- gsl/gsl_byte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index ab5e4e3..9962ab4 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -103,7 +103,7 @@ inline constexpr byte operator~(byte b) noexcept { return byte(~static_cast::value>> inline constexpr IntegerType to_integer(byte b) noexcept { - return {b}; + return static_cast(b); } template -- cgit v1.2.3 From 95f465aecac0c1f77a08ebc06835d03c5371074f Mon Sep 17 00:00:00 2001 From: Michael Balszun Date: Sat, 17 Sep 2016 16:09:21 +0200 Subject: fix wrong types in multi_span_test () --- tests/multi_span_tests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index f04ba84..3bd9bae 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -959,8 +959,8 @@ SUITE(multi_span_tests) } { - auto s1 = nullptr; - auto s2 = nullptr; + multi_span s1 = nullptr; + multi_span s2 = nullptr; CHECK(s1 == s2); CHECK(!(s1 != s2)); CHECK(!(s1 < s2)); -- cgit v1.2.3 From 22c27854529b1e5c5da2b4845ffac764f208326d Mon Sep 17 00:00:00 2001 From: Krzysztof Wrzalik Date: Sun, 18 Sep 2016 17:44:45 +0200 Subject: Added tests for negative multi-span access. --- tests/multi_span_tests.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/multi_span_tests.cpp b/tests/multi_span_tests.cpp index f04ba84..69cb34b 100644 --- a/tests/multi_span_tests.cpp +++ b/tests/multi_span_tests.cpp @@ -1084,6 +1084,12 @@ SUITE(multi_span_tests) CHECK_THROW(av[10][2], fail_fast); CHECK_THROW((av[{10, 2}]), fail_fast); + + CHECK_THROW(av[-1][0], fail_fast); + CHECK_THROW((av[{-1, 0}]), fail_fast); + + CHECK_THROW(av[0][-1], fail_fast); + CHECK_THROW((av[{0, -1}]), fail_fast); } void overloaded_func(multi_span exp, int expected_value) -- cgit v1.2.3 From 6cb0e3082a2b475d2ef5e9b591c60ae98bb3c483 Mon Sep 17 00:00:00 2001 From: Krzysztof Wrzalik Date: Sun, 18 Sep 2016 17:56:18 +0200 Subject: Added a fix for not flagging negative indices to multi_span. --- gsl/multi_span | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gsl/multi_span b/gsl/multi_span index 0abc7c1..40752aa 100644 --- a/gsl/multi_span +++ b/gsl/multi_span @@ -443,7 +443,7 @@ namespace details template size_type linearize(const T& arr) const { - Expects(arr[Dim] < CurrentRange); // Index is out of range + Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); } @@ -1510,7 +1510,7 @@ public: template 1), typename Ret = std::enable_if_t> constexpr Ret operator[](size_type idx) const noexcept { - Expects(idx < bounds_.size()); // index is out of bounds of the array + Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array const size_type ridx = idx * bounds_.stride(); // index is out of bounds of the underlying data -- cgit v1.2.3 From 4e7997190cbc0700f1a796d36a8ee63d3d86f34d Mon Sep 17 00:00:00 2001 From: Michael Balszun Date: Wed, 21 Sep 2016 21:39:28 +0200 Subject: Turn off warning about function-styl casts in gsl_byte --- gsl/gsl_byte | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gsl/gsl_byte b/gsl/gsl_byte index 9962ab4..3f77923 100644 --- a/gsl/gsl_byte +++ b/gsl/gsl_byte @@ -21,6 +21,11 @@ #ifdef _MSC_VER +#pragma warning(push) + +// don't warn about function style casts in byte related operators +#pragma warning(disable : 26493) + // MSVC 2013 workarounds #if _MSC_VER <= 1800 @@ -149,6 +154,8 @@ inline constexpr byte to_byte() noexcept #endif // _MSC_VER <= 1800 +#pragma warning(pop) + #endif // _MSC_VER #endif // GSL_BYTE_H -- cgit v1.2.3 From 67717ea6ae69f391807fec7ebbeb85c135e9facf Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 27 Sep 2016 21:12:51 -0700 Subject: [multi_span] Fix UB hack in static_bounds::operator= * Make BoundsRanges::m_bound non-const and private * Remove various defaulted copy constructor/assignment declarations from BoundsRanges specializations whose only effect is to needlessly suppress the generation of moves * Remove the hackish static_bounds::operator=(const static_bounds&). The implicitly generated default is now sufficient --- gsl/multi_span | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/gsl/multi_span b/gsl/multi_span index 40752aa..2186c7b 100644 --- a/gsl/multi_span +++ b/gsl/multi_span @@ -308,8 +308,6 @@ namespace details { } - BoundsRanges(const BoundsRanges&) = default; - BoundsRanges& operator=(const BoundsRanges&) = default; BoundsRanges(const std::ptrdiff_t* const) {} BoundsRanges() = default; @@ -346,9 +344,9 @@ namespace details static const size_t DynamicNum = Base::DynamicNum + 1; static const size_type CurrentRange = dynamic_range; static const size_type TotalSize = dynamic_range; - const size_type m_bound; - - BoundsRanges(const BoundsRanges&) = default; + private: + size_type m_bound; + public: BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) @@ -420,8 +418,6 @@ namespace details static const size_type TotalSize = Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; - BoundsRanges(const BoundsRanges&) = default; - BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} BoundsRanges() = default; @@ -633,12 +629,6 @@ public: constexpr static_bounds() = default; - constexpr static_bounds& operator=(const static_bounds& otherBounds) - { - new (&m_ranges) MyRanges(otherBounds.m_ranges); - return *this; - } - constexpr sliced_type slice() const noexcept { return sliced_type{static_cast&>(m_ranges)}; -- cgit v1.2.3 From 4b29878d702e4cd243d5bf6f0eb5dcb228301be4 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Wed, 28 Sep 2016 09:43:13 -0600 Subject: Add branch prediction to Ensures / Expects We should be using branch prediction on asserts as these conditions are known to be unlikely, therefore we should be optimizing the likely case. These macros are similar to what the Linux kernel uses. Not really sure what Visual Studio does so at the moment these are disabled for VS. --- gsl/gsl_assert | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gsl/gsl_assert b/gsl/gsl_assert index 10de31a..34b3ecb 100644 --- a/gsl/gsl_assert +++ b/gsl/gsl_assert @@ -38,6 +38,14 @@ #define GSL_STRINGIFY_DETAIL(x) #x #define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) +#if defined(__clang__) || defined(__GNUC__) +#define GSL_LIKELY(x) __builtin_expect (!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect (!!(x), 0) +#else +#define GSL_LIKELY(x) +#define GSL_UNLIKELY(x) +#endif + // // GSL.assert: assertions // @@ -53,19 +61,19 @@ struct fail_fast : public std::runtime_error #if defined(GSL_THROW_ON_CONTRACT_VIOLATION) #define Expects(cond) \ - if (!(cond)) \ + if (GSL_UNLIKELY(!(cond))) \ throw gsl::fail_fast("GSL: Precondition failure at " __FILE__ ": " GSL_STRINGIFY(__LINE__)); #define Ensures(cond) \ - if (!(cond)) \ + if (GSL_UNLIKELY(!(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(); + if (GSL_UNLIKELY(!(cond))) std::terminate(); #define Ensures(cond) \ - if (!(cond)) std::terminate(); + if (GSL_UNLIKELY(!(cond))) std::terminate(); #elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) -- cgit v1.2.3 From 3161d6133fb0c05785e657acd4b178d0253e8319 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Wed, 28 Sep 2016 09:53:33 -0600 Subject: Fix issue with VS builds The Visual Studio part of this patch was wrong, and left an empty if statement. --- gsl/gsl_assert | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gsl/gsl_assert b/gsl/gsl_assert index 34b3ecb..6d8760d 100644 --- a/gsl/gsl_assert +++ b/gsl/gsl_assert @@ -39,11 +39,11 @@ #define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) #if defined(__clang__) || defined(__GNUC__) -#define GSL_LIKELY(x) __builtin_expect (!!(x), 1) -#define GSL_UNLIKELY(x) __builtin_expect (!!(x), 0) +#define GSL_LIKELY(x) __builtin_expect (!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect (!!(x), 0) #else -#define GSL_LIKELY(x) -#define GSL_UNLIKELY(x) +#define GSL_LIKELY(x) (x) +#define GSL_UNLIKELY(x) (x) #endif // -- cgit v1.2.3 From 9e055be6593e11849d4fa7966847054372d025b1 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Wed, 28 Sep 2016 10:00:47 -0600 Subject: Fix compilation issues with Cygwin Cygwin recently did an update that broken string_span again. The original update had to provide a custom strnlen function because Cygwin doesn't implement this (it's a non-std extension). We never added wchar_t support, and the Cygwin update now breaks on exactly this. This patch provides the missing wchar_t. --- gsl/string_span | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/gsl/string_span b/gsl/string_span index cd49998..768bfd5 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -118,6 +118,24 @@ namespace details return len; #endif } + + inline std::size_t wstring_length(const wchar_t *str, std::size_t n) + { +#ifdef GSL_PLATFORM_HAS_STRNLEN + return wcsnlen(str, n); +#else + if (str == nullptr || n == 0) + return 0; + + std::size_t len = 0; + span str_span{str, n}; + + while (len < n && str_span[len]) + len++; + + return len; +#endif + } } // @@ -166,14 +184,14 @@ inline span ensure_z(const char* const& sz, std::ptr inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { - auto len = wcsnlen(sz, narrow_cast(max)); + auto len = details::wstring_length(sz, narrow_cast(max)); Ensures(sz[len] == 0); return {sz, static_cast(len)}; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { - auto len = wcsnlen(sz, narrow_cast(max)); + auto len = details::wstring_length(sz, narrow_cast(max)); Ensures(sz[len] == 0); return {sz, static_cast(len)}; } @@ -230,7 +248,7 @@ namespace details { std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + return narrow_cast(details::wstring_length(ptr, narrow_cast(length))); } }; @@ -248,7 +266,7 @@ namespace details { std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(wcsnlen(ptr, narrow_cast(length))); + return narrow_cast(details::wstring_length(ptr, narrow_cast(length))); } }; } -- cgit v1.2.3 From f953b792f084a7f20e0420e11395888548d5f834 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 27 Sep 2016 22:02:49 -0700 Subject: [Travis] enlarge the support matrix * Build on OSX with Xcode 8 * Build on Linux with Clang 3.6/7/8 and libc++ * Build on Linux with GCC 5/6 --- .travis.yml | 135 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3fed41b..5c69187 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,65 +3,140 @@ language: cpp sudo: false +cache: + directories: + - ${TRAVIS_BUILD_DIR}/deps/cmake + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.5.2/install + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.6.2/install + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.7.1/install + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.8.1/install + - ${TRAVIS_BUILD_DIR}/deps/llvm-3.9.0/install + matrix: include: - - env: COMPILER=clang++-3.6 BUILD_TYPE=Debug CLANG=1 + - env: BUILD_TYPE=Debug + os: osx + osx_image: xcode8 + compiler: clang + - env: BUILD_TYPE=Release + os: osx + osx_image: xcode8 compiler: clang + - env: CLANG_VERSION=3.6 BUILD_TYPE=Debug + os: linux addons: &clang36 apt: packages: - clang-3.6 - - cmake - g++-5 sources: &sources - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.6 - - kalakris-cmake - - env: COMPILER=clang++-3.6 BUILD_TYPE=Release CLANG=1 - compiler: clang + - env: CLANG_VERSION=3.6 BUILD_TYPE=Release + os: linux addons: *clang36 - - env: COMPILER=g++-5 BUILD_TYPE=Debug - compiler: gcc + - env: CLANG_VERSION=3.7 BUILD_TYPE=Debug + os: linux + addons: &clang37 + apt: + packages: + - clang-3.7 + - g++-5 + sources: &sources + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + - env: CLANG_VERSION=3.7 BUILD_TYPE=Release + os: linux + addons: *clang37 + - env: CLANG_VERSION=3.8 BUILD_TYPE=Debug + os: linux + addons: &clang38 + apt: + packages: + - clang-3.8 + - g++-5 + sources: &sources + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + - env: CLANG_VERSION=3.8 BUILD_TYPE=Release + os: linux + addons: *clang38 + - env: GCC_VERSION=5 BUILD_TYPE=Debug + os: linux addons: &gcc5 apt: packages: g++-5 sources: *sources - - env: COMPILER=g++-5 BUILD_TYPE=Release - compiler: gcc + - env: GCC_VERSION=5 BUILD_TYPE=Release + os: linux addons: *gcc5 + - env: GCC_VERSION=6 BUILD_TYPE=Debug + os: linux + addons: &gcc6 + apt: + packages: g++-6 + sources: *sources + - env: GCC_VERSION=6 BUILD_TYPE=Release + os: linux + addons: *gcc6 install: - - which $COMPILER + - if [[ -n "$CLANG_VERSION" ]]; then export CXX=clang++-$CLANG_VERSION CC=clang-$CLANG_VERSION; fi + - if [[ -n "$GCC_VERSION" ]]; then export CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION; fi + - JOBS=2 - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" - - mkdir ${DEPS_DIR} && cd ${DEPS_DIR} + - mkdir -p "${DEPS_DIR}" && cd "${DEPS_DIR}" + + ############################################################################ + # Install a recent CMake (unless already installed on OS X) + ############################################################################ + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + if [[ -z "$(ls -A ${DEPS_DIR}/cmake/bin)" ]]; then + CMAKE_URL="https://cmake.org/files/v3.6/cmake-3.6.2-Linux-x86_64.tar.gz" + mkdir -p cmake && travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C cmake + fi + export PATH="${DEPS_DIR}/cmake/bin:${PATH}" + else + if ! brew ls --version cmake &>/dev/null; then brew install cmake; fi + fi + + ############################################################################ + # [linux]: Install the right version of libc++ + ############################################################################ - | - if [[ "$CLANG" == 1 && "${TRAVIS_OS_NAME}" == "linux" && "${STDLIB}" != "libstdc++" ]]; then - if [[ "${COMPILER}" == "clang++-3.5" ]]; then LLVM_VERSION="3.5.2"; fi - if [[ "${COMPILER}" == "clang++-3.6" ]]; then LLVM_VERSION="3.6.2"; fi - if [[ "${COMPILER}" == "clang++-3.7" ]]; then LLVM_VERSION="3.7.0"; fi + if [[ -n "$CLANG_VERSION" && "${TRAVIS_OS_NAME}" == "linux" && "${STDLIB}" != "libstdc++" ]]; then + if [[ "$CLANG_VERSION" == "3.5" ]]; then LLVM_VERSION="3.5.2"; fi + if [[ "$CLANG_VERSION" == "3.6" ]]; then LLVM_VERSION="3.6.2"; fi + if [[ "$CLANG_VERSION" == "3.7" ]]; then LLVM_VERSION="3.7.1"; fi + if [[ "$CLANG_VERSION" == "3.8" ]]; then LLVM_VERSION="3.8.1"; fi + if [[ "$CLANG_VERSION" == "3.9" ]]; then LLVM_VERSION="3.9.0"; fi + LLVM_ROOT="${DEPS_DIR}/llvm-${LLVM_VERSION}" LLVM_URL="http://llvm.org/releases/${LLVM_VERSION}/llvm-${LLVM_VERSION}.src.tar.xz" LIBCXX_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxx-${LLVM_VERSION}.src.tar.xz" LIBCXXABI_URL="http://llvm.org/releases/${LLVM_VERSION}/libcxxabi-${LLVM_VERSION}.src.tar.xz" - mkdir -p llvm llvm/build llvm/projects/libcxx llvm/projects/libcxxabi - travis_retry wget --quiet -O - ${LLVM_URL} | tar --strip-components=1 -xJ -C llvm - travis_retry wget --quiet -O - ${LIBCXX_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxx - travis_retry wget --quiet -O - ${LIBCXXABI_URL} | tar --strip-components=1 -xJ -C llvm/projects/libcxxabi - (cd llvm/build && cmake .. -DCMAKE_INSTALL_PREFIX=${DEPS_DIR}/llvm/install -DCMAKE_CXX_COMPILER=clang++) - (cd llvm/build/projects/libcxx && make install -j2) - (cd llvm/build/projects/libcxxabi && make install -j2) - export CXXFLAGS="-I ${DEPS_DIR}/llvm/install/include/c++/v1" - export LDFLAGS="-L ${DEPS_DIR}/llvm/install/lib -l c++ -l c++abi" - export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${DEPS_DIR}/llvm/install/lib" + if [[ -z "$(ls -A ${LLVM_ROOT}/install/include)" ]]; then + mkdir -p "${LLVM_ROOT}" "${LLVM_ROOT}/build" "${LLVM_ROOT}/projects/libcxx" "${LLVM_ROOT}/projects/libcxxabi" + travis_retry wget --quiet -O - "${LLVM_URL}" | tar --strip-components=1 -xJ -C "${LLVM_ROOT}" + travis_retry wget --quiet -O - "${LIBCXX_URL}" | tar --strip-components=1 -xJ -C "${LLVM_ROOT}/projects/libcxx" + travis_retry wget --quiet -O - "${LIBCXXABI_URL}" | tar --strip-components=1 -xJ -C "${LLVM_ROOT}/projects/libcxxabi" + (cd "${LLVM_ROOT}/build" && cmake .. -DCMAKE_CXX_COMPILER="$CXX" -DCMAKE_C_COMPILER="$CC" -DCMAKE_INSTALL_PREFIX="${LLVM_ROOT}/install" -DCMAKE_BUILD_TYPE=$BUILD_TYPE) + (cd "${LLVM_ROOT}/build/projects/libcxx" && make install -j$JOBS) + (cd "${LLVM_ROOT}/build/projects/libcxxabi" && make install -j$JOBS) + fi + export CXXFLAGS="-I ${LLVM_ROOT}/install/include/c++/v1" + export LDFLAGS="-L ${LLVM_ROOT}/install/lib -lc++ -lc++abi" + export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${LLVM_ROOT}/install/lib" fi before_script: - - cd ${TRAVIS_BUILD_DIR} - - cmake -H. -Bb -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_INSTALL_PREFIX=$PWD/o -DCMAKE_BUILD_TYPE=$BUILD_TYPE - - cmake --build b + - cd "${TRAVIS_BUILD_DIR}" + - cmake . -Bbuild -DCMAKE_CXX_COMPILER="$CXX" -DCMAKE_C_COMPILER="$CC" -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - cmake --build build -- -j$JOBS script: - - cd b - - ctest + - cd build + - ctest --output-on-failure -j$JOBS notifications: email: false -- cgit v1.2.3 From 612747a5e1174760d71f0564a55eb4761375c709 Mon Sep 17 00:00:00 2001 From: Gary Furnish Date: Tue, 4 Oct 2016 21:13:18 -0600 Subject: For span, fix size_t to index_type conversion warning. Use narrow per discussion instead of static_cast. --- gsl/span | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gsl/span b/gsl/span index a9a3322..62b63de 100644 --- a/gsl/span +++ b/gsl/span @@ -392,7 +392,7 @@ public: std::is_convertible::value && std::is_convertible().data())>::value>> - constexpr span(Container& cont) : span(cont.data(), cont.size()) + constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) { } @@ -402,7 +402,7 @@ public: std::is_convertible::value && std::is_convertible().data())>::value>> - constexpr span(const Container& cont) : span(cont.data(), cont.size()) + constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) { } -- cgit v1.2.3 From 22a286cefe552a16ad1e7fb3013bd228d2804584 Mon Sep 17 00:00:00 2001 From: Gary Furnish Date: Sun, 9 Oct 2016 18:01:46 -0600 Subject: Fix #388 Deprecated implicit copy assignment operator for span_iterator in C++17. --- gsl/span | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gsl/span b/gsl/span index 62b63de..6f74dc5 100644 --- a/gsl/span +++ b/gsl/span @@ -163,6 +163,8 @@ namespace details { } + constexpr span_iterator& operator=(const span_iterator&) noexcept = default; + constexpr reference operator*() const { Expects(span_); -- cgit v1.2.3 From 1287e624cd3e23c7a3edc036de59690d733815d3 Mon Sep 17 00:00:00 2001 From: MikeGitb Date: Mon, 17 Oct 2016 20:36:11 +0100 Subject: Address #313: try to guard against strict-aliasing bugs with gsl::byte * Add test to demonstrate byte aliasing problem on g++ and clang++ * Add note about no-strict-aliasing flag in README * Activate aliasing unit test and use -fno-strict-aliasing flag --- README.md | 4 +++- tests/CMakeLists.txt | 4 ++-- tests/byte_tests.cpp | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9673df5..c3aff66 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope # Quick Start ## Supported Platforms -The test suite that exercises GSL has been built and passes successfully on the following platforms: +The test suite that exercises GSL has been built and passes successfully on the following platforms:1) * Windows using Visual Studio 2013 * Windows using Visual Studio 2015 @@ -34,6 +34,8 @@ The test suite that exercises GSL has been built and passes successfully on the > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider contributing any changes that were necessary back to this project to benefit the wider community. +1) For `gsl::byte` to work correctly with Clang and GCC you might have to use the ` -fno-strict-aliasing` compiler option. + ## Building the tests To build the tests, you will require the following: diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3c3125e..e480474 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,9 +24,9 @@ else() 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 -Wall -Wno-missing-braces") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -std=c++14 -O3 -Wall -Wno-missing-braces") elseif(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-missing-braces") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -std=c++11 -O3 -Wall -Wno-missing-braces") else() message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") endif() diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 3bbf382..8cb0da8 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -43,7 +43,7 @@ SUITE(byte_tests) byte b = byte(12); CHECK(static_cast(b) == 12); } - + { byte b = to_byte<12>(); CHECK(static_cast(b) == 12); @@ -114,6 +114,20 @@ SUITE(byte_tests) // CHECK(0x12 == gsl::to_integer(b)); // expect compile-time error // CHECK(0x12 == gsl::to_integer(b)); // expect compile-time error } + + int modify_both(gsl::byte& b, int& i) + { + i = 10; + b = to_byte<5>(); + return i; + } + + TEST(aliasing) + { + int i{ 0 }; + int res = modify_both(reinterpret_cast(i), i); + CHECK(res == i); + } } } -- cgit v1.2.3 From a14f27474f09e2c2b5360cb07d777b0ca6ee68b3 Mon Sep 17 00:00:00 2001 From: Josaphat Valdivia Date: Mon, 17 Oct 2016 15:41:24 -0400 Subject: [#391] string_length: Remove use of strnlen and use consistent length type. * Removes reference to strnlen as per #391 * Use ptrdiff for string_length interfaces for #391 --- gsl/string_span | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/gsl/string_span b/gsl/string_span index 768bfd5..fb943dc 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -56,10 +56,6 @@ #endif // _MSC_VER <= 1800 #endif // _MSC_VER -#ifndef __CYGWIN__ -#define GSL_PLATFORM_HAS_STRNLEN -#endif - // In order to test the library, we need it to throw exceptions that we can catch #ifdef GSL_THROW_ON_CONTRACT_VIOLATION @@ -101,40 +97,32 @@ using wzstring = basic_zstring; namespace details { - inline std::size_t string_length(const char *str, std::size_t n) + inline std::ptrdiff_t string_length(const char *str, std::ptrdiff_t n) { -#ifdef GSL_PLATFORM_HAS_STRNLEN - return strnlen(str, n); -#else - if (str == nullptr || n == 0) + if (str == nullptr || n <= 0) return 0; - std::size_t len = 0; span str_span{str, n}; + std::ptrdiff_t len = 0; while (len < n && str_span[len]) len++; return len; -#endif } - inline std::size_t wstring_length(const wchar_t *str, std::size_t n) + inline std::ptrdiff_t wstring_length(const wchar_t *str, std::ptrdiff_t n) { -#ifdef GSL_PLATFORM_HAS_STRNLEN - return wcsnlen(str, n); -#else - if (str == nullptr || n == 0) + if (str == nullptr || n <= 0) return 0; - std::size_t len = 0; span str_span{str, n}; + std::ptrdiff_t len = 0; while (len < n && str_span[len]) len++; return len; -#endif } } @@ -170,30 +158,30 @@ inline span ensure_z(T* const& sz, std::ptrdiff_t max = PTRDI // overloads to share an implementation inline span ensure_z(char* const& sz, std::ptrdiff_t max) { - auto len = details::string_length(sz, narrow_cast(max)); + auto len = details::string_length(sz, max); Ensures(sz[len] == 0); - return {sz, static_cast(len)}; + return {sz, len}; } inline span ensure_z(const char* const& sz, std::ptrdiff_t max) { - auto len = details::string_length(sz, narrow_cast(max)); + auto len = details::string_length(sz, max); Ensures(sz[len] == 0); - return {sz, static_cast(len)}; + return {sz, len}; } inline span ensure_z(wchar_t* const& sz, std::ptrdiff_t max) { - auto len = details::wstring_length(sz, narrow_cast(max)); + auto len = details::wstring_length(sz, max); Ensures(sz[len] == 0); - return {sz, static_cast(len)}; + return {sz, len}; } inline span ensure_z(const wchar_t* const& sz, std::ptrdiff_t max) { - auto len = details::wstring_length(sz, narrow_cast(max)); + auto len = details::wstring_length(sz, max); Ensures(sz[len] == 0); - return {sz, static_cast(len)}; + return {sz, len}; } template @@ -239,7 +227,7 @@ namespace details { std::ptrdiff_t operator()(char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(details::string_length(ptr, narrow_cast(length))); + return details::string_length(ptr, length); } }; @@ -248,7 +236,7 @@ namespace details { std::ptrdiff_t operator()(wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(details::wstring_length(ptr, narrow_cast(length))); + return details::wstring_length(ptr, length); } }; @@ -257,7 +245,7 @@ namespace details { std::ptrdiff_t operator()(const char* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(details::string_length(ptr, narrow_cast(length))); + return details::string_length(ptr, length); } }; @@ -266,7 +254,7 @@ namespace details { std::ptrdiff_t operator()(const wchar_t* const ptr, std::ptrdiff_t length) noexcept { - return narrow_cast(details::wstring_length(ptr, narrow_cast(length))); + return details::wstring_length(ptr, length); } }; } -- cgit v1.2.3 From 1e95421889b3f14224cd02984bf713375092f06d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 17 Oct 2016 21:42:58 +0200 Subject: [#392] Don't install tests 'make install' should install the GSL library (ie. the headers), not the tests. It still installs the UnitTest++ headers, but that will be more complex to fix, as GSL just imports UnitTest++ as a git submodule, and the install command propagates down to UnitTest++'s CMakeLists.txt. --- tests/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e480474..44db32d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,9 +35,6 @@ endif() function(add_gsl_test name) add_executable(${name} ${name}.cpp ../gsl/gsl ../gsl/gsl_assert ../gsl/gsl_util ../gsl/multi_span ../gsl/span ../gsl/string_span) target_link_libraries(${name} UnitTest++) - install(TARGETS ${name} - RUNTIME DESTINATION bin - ) add_test( ${name} ${name} @@ -54,4 +51,4 @@ add_gsl_test(notnull_tests) add_gsl_test(assertion_tests) add_gsl_test(utils_tests) add_gsl_test(owner_tests) -add_gsl_test(byte_tests) \ No newline at end of file +add_gsl_test(byte_tests) -- cgit v1.2.3 From b07383ead13d46ead9090fcd80dbd79ae6154bac Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Tue, 18 Oct 2016 12:52:45 -0600 Subject: [gsl_util] Update narrow_cast to use std::forward * Update narrow_cast to use std::forward Based on [F19](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#f19-for-forward-parameters-pass-by-tp-and-only-stdforward-the-parameter), I believe `gsl::narrow_cast` should be implemented using forward semantics. * Fix for VS 2013 --- gsl/gsl_util | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gsl/gsl_util b/gsl/gsl_util index 17a1edb..f0ac964 100644 --- a/gsl/gsl_util +++ b/gsl/gsl_util @@ -93,11 +93,19 @@ inline final_act finally(F&& f) noexcept } // narrow_cast(): a searchable way to do narrowing casts of values +#if _MSC_VER <= 1800 template inline constexpr T narrow_cast(U u) noexcept { return static_cast(u); } +#else +template +inline constexpr T narrow_cast(U&& u) noexcept +{ + return static_cast(std::forward(u)); +} +#endif struct narrowing_error : public std::exception { -- cgit v1.2.3 From 6cffe0d14cbc0540e16939aaffbee3451fa0a131 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Wed, 26 Oct 2016 15:11:24 -0600 Subject: Adds gsl::span::at() As per this discussion: https://github.com/Microsoft/GSL/issues/402 This patch adds support for at() to gsl::span --- gsl/span | 2 ++ tests/span_tests.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/gsl/span b/gsl/span index 6f74dc5..cf90e86 100644 --- a/gsl/span +++ b/gsl/span @@ -503,6 +503,8 @@ public: Expects(idx >= 0 && idx < storage_.size()); return data()[idx]; } + + constexpr reference at(index_type idx) const { return this->operator[](idx); } constexpr reference operator()(index_type idx) const { return this->operator[](idx); } constexpr pointer data() const noexcept { return storage_.data(); } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index a1dd64d..35e6b03 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -769,6 +769,25 @@ SUITE(span_tests) } } + TEST(at_call) + { + int arr[4] = {1, 2, 3, 4}; + + { + span s = arr; + CHECK(s.at(0) == 1); + CHECK_THROW(s.at(5), fail_fast); + } + + { + int arr2d[2] = {1, 6}; + span s = arr2d; + CHECK(s.at(0) == 1); + CHECK(s.at(1) == 6); + CHECK_THROW(s.at(2) ,fail_fast); + } + } + TEST(operator_function_call) { int arr[4] = {1, 2, 3, 4}; -- cgit v1.2.3 From f4486389b87b1758caffe4621ed45754e698fa47 Mon Sep 17 00:00:00 2001 From: Rian Quinn Date: Fri, 28 Oct 2016 00:45:54 -0600 Subject: Fix overflow found by GCC in basic_zstring_span::as_string_span(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes an overflow that was identified with strict overflow warnings enabled, and optimizations turned on Signed-off-by: “Rian <“rianquinn@gmail.com”> --- gsl/string_span | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gsl/string_span b/gsl/string_span index fb943dc..703bc01 100644 --- a/gsl/string_span +++ b/gsl/string_span @@ -554,7 +554,8 @@ public: constexpr string_span_type as_string_span() const noexcept { - return span_.first(span_.size() - 1); + auto sz = span_.size(); + return span_.first(sz <= 0 ? 0 : sz - 1); } constexpr string_span_type ensure_z() const noexcept { return gsl::ensure_z(span_); } -- cgit v1.2.3