aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil MacIntosh <neilmac@microsoft.com>2015-08-20 18:09:14 -0700
committerNeil MacIntosh <neilmac@microsoft.com>2015-08-20 18:09:14 -0700
commita9dcbe04ff330ef8297191d19951d4a313b2115a (patch)
treef705d0cad84b2ab25449fe272104d1eb8a014f1f
parent836832914241ea03102ff12293b0d4d47ea6a9a0 (diff)
downloadplatform_external_Microsoft-GSL-a9dcbe04ff330ef8297191d19951d4a313b2115a.tar.gz
platform_external_Microsoft-GSL-a9dcbe04ff330ef8297191d19951d4a313b2115a.tar.bz2
platform_external_Microsoft-GSL-a9dcbe04ff330ef8297191d19951d4a313b2115a.zip
Initial commit of library files.
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt11
-rw-r--r--LICENSE11
-rw-r--r--README.md64
-rw-r--r--include/array_view.h2113
-rw-r--r--include/fail_fast.h39
-rw-r--r--include/gsl.h284
-rw-r--r--include/string_view.h178
-rw-r--r--tests/CMakeLists.txt143
-rw-r--r--tests/array_view_tests.cpp648
-rw-r--r--tests/assertion_tests.cpp53
-rw-r--r--tests/at_tests.cpp60
-rw-r--r--tests/bounds_tests.cpp99
-rw-r--r--tests/maybenull_tests.cpp197
-rw-r--r--tests/notnull_tests.cpp87
-rw-r--r--tests/string_view_tests.cpp104
-rw-r--r--tests/utils_tests.cpp87
17 files changed, 4177 insertions, 2 deletions
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 <new>
+#include <stdexcept>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+#include <utility>
+#include <array>
+#include <iterator>
+#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 <typename SizeType>
+ struct SizeTypeTraits
+ {
+ static const size_t max_value = std::is_signed<SizeType>::value ? static_cast<typename std::make_unsigned<SizeType>::type>(-1) / 2 : static_cast<SizeType>(-1);
+ };
+
+
+ template <typename ConcreteType, typename ValueType, unsigned int Rank>
+ class coordinate_facade
+ {
+ static_assert(std::is_integral<ValueType>::value
+ && sizeof(ValueType) <= sizeof(size_t), "ValueType must be unsigned integral type!");
+ static_assert(Rank > 0, "Rank must be greater than 0!");
+
+ template <typename OtherConcreteType, typename OtherValueType, unsigned int OtherRank>
+ 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<coordinate_facade, ConcreteType>::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<coordinate_facade, ConcreteType>::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<value_type> il)
+ {
+ static_assert(std::is_base_of<coordinate_facade, ConcreteType>::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 <typename OtherConcreteType, typename OtherValueType>
+ _CONSTEXPR coordinate_facade(const coordinate_facade<OtherConcreteType, OtherValueType, Rank> & other)
+ {
+ for (unsigned int i = 0; i < rank; ++i)
+ {
+ fail_fast_assert(static_cast<size_t>(other.elems[i]) <= SizeTypeTraits<value_type>::max_value);
+ elems[i] = static_cast<value_type>(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<const ConcreteType&>(*this);
+ }
+ _CONSTEXPR ConcreteType& to_concrete() _NOEXCEPT
+ {
+ return static_cast<ConcreteType&>(*this);
+ }
+ };
+ template <typename T>
+ 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 <unsigned int Rank, typename ValueType = size_t>
+class index : private details::coordinate_facade<index<Rank, ValueType>, ValueType, Rank>
+{
+ using Base = details::coordinate_facade<index<Rank, ValueType>, 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 <bool Enabled = rank == 1, typename = std::enable_if_t<Enabled>>
+ _CONSTEXPR index(value_type e0) _NOEXCEPT : Base(e0){}
+ _CONSTEXPR index(std::initializer_list<value_type> il) : Base(il){}
+
+ _CONSTEXPR index(const index &) = default;
+
+ template <typename OtherValueType>
+ _CONSTEXPR index(const index<Rank, OtherValueType> &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 <typename ValueType>
+class index<1, ValueType>
+{
+ template <unsigned int, typename OtherValueType>
+ 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<value_type> il)
+ {
+ fail_fast_assert(il.size() == rank);
+ value = begin(il)[0];
+ }
+
+ _CONSTEXPR index(const index &) = default;
+
+ template <typename OtherValueType>
+ _CONSTEXPR index(const index<1, OtherValueType> & other)
+ {
+ fail_fast_assert(other.value <= details::SizeTypeTraits<ValueType>::max_value);
+ value = static_cast<ValueType>(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 <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+ constexpr operator T() const noexcept
+ {
+ return static_cast<T>(-1);
+ }
+
+ template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+ constexpr bool operator ==(T other) const noexcept
+ {
+ return static_cast<T>(-1) == other;
+ }
+
+ template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+ constexpr bool operator !=(T other) const noexcept
+ {
+ return static_cast<T>(-1) != other;
+ }
+
+};
+
+template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
+constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept
+{
+ return right == left;
+}
+
+template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::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 <typename SizeType, SizeType Fact1, SizeType Fact2, SizeType ConstBound>
+ struct StaticSizeHelperImpl
+ {
+ static_assert(static_cast<size_t>(Fact1) * static_cast<size_t>(Fact2) <= SizeTypeTraits<SizeType>::max_value, "Value out of the range of SizeType");
+ static const SizeType value = Fact1 * Fact2;
+ };
+
+ template <typename SizeType, SizeType Fact1, SizeType ConstBound>
+ struct StaticSizeHelperImpl<SizeType, Fact1, ConstBound, ConstBound>
+ {
+ static const SizeType value = ConstBound;
+ };
+
+ template <typename SizeType, SizeType Fact2, SizeType ConstBound>
+ struct StaticSizeHelperImpl<SizeType, ConstBound, Fact2, ConstBound>
+ {
+ static const SizeType value = ConstBound;
+ };
+
+ template <typename SizeType, SizeType ConstBound>
+ struct StaticSizeHelperImpl<SizeType, ConstBound, ConstBound, ConstBound>
+ {
+ static const SizeType value = static_cast<SizeType>(ConstBound);
+ };
+
+ template <typename SizeType, SizeType Fact1, SizeType Fact2>
+ struct StaticSizeHelper
+ {
+ static const SizeType value = StaticSizeHelperImpl<SizeType, static_cast<SizeType>(Fact1), static_cast<SizeType>(Fact2), static_cast<SizeType>(dynamic_range)>::value;
+ };
+
+
+ template <size_t Left, size_t Right>
+ struct LessThan
+ {
+ static const bool value = Left < Right;
+ };
+
+ template <typename SizeType, size_t... Ranges>
+ 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 <typename OtherType>
+ BoundsRanges (const OtherType &, bool firstLevel) {}
+ BoundsRanges(const SizeType * const arr) { }
+ BoundsRanges() = default;
+
+
+ template <typename T, unsigned int Dim>
+ void serialize(T &) const {
+ }
+ template <typename T, unsigned int Dim>
+ SizeType linearize(const T &) const {
+ return 0;
+ }
+ template <typename T, unsigned int Dim>
+ ptrdiff_t contains(const T &) const {
+ return 0;
+ }
+
+ size_t totalSize() const _NOEXCEPT {
+ return TotalSize;
+ }
+
+ bool operator == (const BoundsRanges &) const _NOEXCEPT
+ {
+ return true;
+ }
+ };
+
+ template <typename SizeType, size_t... RestRanges>
+ struct BoundsRanges <SizeType, dynamic_range, RestRanges...> : BoundsRanges<SizeType, RestRanges...>{
+ using Base = BoundsRanges <SizeType, RestRanges... >;
+ 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<SizeType>(*arr * this->Base::totalSize()))
+ {
+ fail_fast_assert(0 <= *arr);
+ fail_fast_assert(*arr * this->Base::totalSize() <= details::SizeTypeTraits<SizeType>::max_value);
+ }
+ BoundsRanges() : m_bound(0) {}
+
+ template <typename OtherSizeType, size_t OtherRange, size_t... RestOtherRanges>
+ BoundsRanges(const BoundsRanges<OtherSizeType, OtherRange, RestOtherRanges...> &other, bool firstLevel = true) :
+ Base(static_cast<const BoundsRanges<OtherSizeType, RestOtherRanges...>&>(other), false), m_bound (static_cast<SizeType>(other.totalSize()))
+ {
+ }
+
+ template <typename T, unsigned int Dim = 0>
+ void serialize(T & arr) const {
+ arr[Dim] = elementNum();
+ this->Base::template serialize<T, Dim + 1>(arr);
+ }
+ template <typename T, unsigned int Dim = 0>
+ SizeType linearize(const T & arr) const {
+ const size_t index = this->Base::totalSize() * arr[Dim];
+ fail_fast_assert(index < static_cast<size_t>(m_bound));
+ return static_cast<SizeType>(index) + this->Base::template linearize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, unsigned int Dim = 0>
+ ptrdiff_t contains(const T & arr) const {
+ const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
+ if (last == -1)
+ return -1;
+ const ptrdiff_t cur = this->Base::totalSize() * arr[Dim];
+ return static_cast<size_t>(cur) < static_cast<size_t>(m_bound) ? cur + last : -1;
+ }
+
+ size_t totalSize() const _NOEXCEPT {
+ return m_bound;
+ }
+
+ SizeType elementNum() const _NOEXCEPT {
+ return static_cast<SizeType>(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<const Base &>(*this) == static_cast<const Base &>(rhs);
+ }
+ };
+
+ template <typename SizeType, size_t CurRange, size_t... RestRanges>
+ struct BoundsRanges <SizeType, CurRange, RestRanges...> : BoundsRanges<SizeType, RestRanges...>{
+ using Base = BoundsRanges <SizeType, RestRanges... >;
+ static const unsigned int Depth = Base::Depth + 1;
+ static const unsigned int DynamicNum = Base::DynamicNum;
+ static const SizeType CurrentRange = static_cast<SizeType>(CurRange);
+ static const SizeType TotalSize = StaticSizeHelper<SizeType, Base::TotalSize, CurrentRange>::value;
+ static_assert (CurRange <= SizeTypeTraits<SizeType>::max_value, "CurRange must be smaller than SizeType limits");
+
+ BoundsRanges (const BoundsRanges &) = default;
+ BoundsRanges(const SizeType * const arr) : Base(arr) { }
+ BoundsRanges() = default;
+
+ template <typename OtherSizeType, size_t OtherRange, size_t... RestOtherRanges>
+ BoundsRanges(const BoundsRanges<OtherSizeType, OtherRange, RestOtherRanges...> &other, bool firstLevel = true) : Base(static_cast<const BoundsRanges<OtherSizeType, RestOtherRanges...>&>(other), false)
+ {
+ fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize());
+ }
+
+ template <typename T, unsigned int Dim = 0>
+ void serialize(T & arr) const {
+ arr[Dim] = elementNum();
+ this->Base::template serialize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, unsigned int Dim = 0>
+ SizeType linearize(const T & arr) const {
+ fail_fast_assert(arr[Dim] < CurrentRange);
+ return static_cast<SizeType>(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize<T, Dim + 1>(arr);
+ }
+
+ template <typename T, unsigned int Dim = 0>
+ ptrdiff_t contains(const T & arr) const {
+ if (static_cast<size_t>(arr[Dim]) >= CurrentRange)
+ return -1;
+ const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
+ if (last == -1)
+ return -1;
+ return static_cast<ptrdiff_t>(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<const Base &>(*this) == static_cast<const Base &>(rhs);
+ }
+ };
+
+ template <typename SourceType, typename TargetType, size_t Rank>
+ struct BoundsRangeConvertible2;
+
+ // TODO: I have to rewrite BoundsRangeConvertible into following way to workaround VS 2013 bugs
+ template <size_t Rank, typename SourceType, typename TargetType, typename Ret = BoundsRangeConvertible2<typename SourceType::Base, typename TargetType::Base, Rank>>
+ auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
+
+ template <size_t Rank, typename SourceType, typename TargetType>
+ auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
+
+ template <typename SourceType, typename TargetType, size_t Rank>
+ struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible<Rank - 1>(SourceType(), TargetType(),
+ std::integral_constant<bool, SourceType::Depth == TargetType::Depth
+ && (SourceType::CurrentRange == TargetType::CurrentRange || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>()))
+ {};
+
+ template <typename SourceType, typename TargetType>
+ struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type {};
+
+ template <typename SourceType, typename TargetType, size_t Rank = TargetType::Depth>
+ struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible<Rank - 1>(SourceType(), TargetType(),
+ std::integral_constant<bool, SourceType::Depth == TargetType::Depth
+ && (!LessThan<size_t(SourceType::CurrentRange), size_t(TargetType::CurrentRange)>::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>()))
+ {};
+ template <typename SourceType, typename TargetType>
+ struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type {};
+
+ template <typename TypeChain>
+ struct TypeListIndexer
+ {
+ const TypeChain & obj;
+ TypeListIndexer(const TypeChain & obj) :obj(obj){}
+ template<unsigned int N>
+ const TypeChain & getObj(std::true_type)
+ {
+ return obj;
+ }
+ template<unsigned int N, typename MyChain = TypeChain, typename MyBase = typename MyChain::Base>
+ auto getObj(std::false_type) -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase &>(obj)).template get<N>())
+ {
+ return TypeListIndexer<MyBase>(static_cast<const MyBase &>(obj)).template get<N>();
+ }
+ template <unsigned int N>
+ auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, true>()))
+ {
+ return getObj<N - 1>(std::integral_constant<bool, N == 0>());
+ }
+ };
+
+ template <typename TypeChain>
+ TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain &obj)
+ {
+ return TypeListIndexer<TypeChain>(obj);
+ }
+}
+
+template <typename IndexType>
+class bounds_iterator;
+
+template <typename SizeType, size_t... Ranges>
+class static_bounds {
+public:
+ static_bounds(const details::BoundsRanges<SizeType, Ranges...> &empty) {
+ }
+};
+
+template <typename SizeType, size_t FirstRange, size_t... RestRanges>
+class static_bounds<SizeType, FirstRange, RestRanges...>
+{
+ using MyRanges = details::BoundsRanges <SizeType, FirstRange, RestRanges... >;
+ static_assert(std::is_integral<SizeType>::value
+ && details::SizeTypeTraits<SizeType>::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 <typename SizeType2, size_t... Ranges2>
+ 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<SizeType>(MyRanges::TotalSize);
+
+ using size_type = SizeType;
+ using index_type = index<rank, size_type>;
+ using iterator = bounds_iterator<index_type>;
+ using const_iterator = bounds_iterator<index_type>;
+ using difference_type = ptrdiff_t;
+ using sliced_type = static_bounds<SizeType, RestRanges...>;
+ using mapping_type = contiguous_mapping_tag;
+public:
+ _CONSTEXPR static_bounds(const static_bounds &) = default;
+
+ template <typename OtherSizeType, size_t... Ranges, typename Dummy = std::enable_if_t<
+ details::BoundsRangeConvertible<details::BoundsRanges<OtherSizeType, Ranges...>, details::BoundsRanges <SizeType, FirstRange, RestRanges... >>::value>>
+ _CONSTEXPR static_bounds(const static_bounds<OtherSizeType, Ranges...> &other):
+ m_ranges(other.m_ranges)
+ {
+ }
+
+ _CONSTEXPR static_bounds(std::initializer_list<size_type> il) : m_ranges(il.begin())
+ {
+ fail_fast_assert(MyRanges::DynamicNum == il.size() && m_ranges.totalSize() <= details::SizeTypeTraits<size_type>::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<const details::BoundsRanges<SizeType, RestRanges...> &>(m_ranges)};
+ }
+
+ _CONSTEXPR size_type stride() const _NOEXCEPT
+ {
+ return rank > 1 ? slice().size() : 1;
+ }
+
+ _CONSTEXPR size_type size() const _NOEXCEPT
+ {
+ return static_cast<size_type>(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 <unsigned int Dim = 0>
+ _CONSTEXPR size_type extent() const _NOEXCEPT
+ {
+ return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum();
+ }
+
+ _CONSTEXPR index_type index_bounds() const _NOEXCEPT
+ {
+ index_type extents;
+ m_ranges.serialize(extents);
+ return extents;
+ }
+
+ template <typename OtherSizeTypes, size_t... Ranges>
+ _CONSTEXPR bool operator == (const static_bounds<OtherSizeTypes, Ranges...> & rhs) const _NOEXCEPT
+ {
+ return this->size() == rhs.size();
+ }
+
+ template <typename OtherSizeTypes, size_t... Ranges>
+ _CONSTEXPR bool operator != (const static_bounds<OtherSizeTypes, Ranges...> & 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 <unsigned int Rank, typename SizeType = size_t>
+class strided_bounds : private details::coordinate_facade<strided_bounds<Rank>, SizeType, Rank>
+{
+ using Base = details::coordinate_facade<strided_bounds<Rank>, 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<rank, size_type>;
+ using iterator = bounds_iterator<index_type>;
+ using const_iterator = bounds_iterator<index_type>;
+ static const int dynamic_rank = rank;
+ static const size_t static_size = dynamic_range;
+ using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
+ using mapping_type = generalized_mapping_tag;
+ _CONSTEXPR strided_bounds() _NOEXCEPT : Base(), strides() {}
+ _CONSTEXPR strided_bounds(const strided_bounds &) = default;
+
+ template <typename OtherSizeType>
+ _CONSTEXPR strided_bounds(const strided_bounds<rank, OtherSizeType> &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<value_type> 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 <unsigned int Dim = 0>
+ _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 <typename T>
+struct is_bounds : std::integral_constant<bool, false> {};
+template <typename SizeType, size_t... Ranges>
+struct is_bounds<static_bounds<SizeType, Ranges...>> : std::integral_constant<bool, true> {};
+template <unsigned int Rank, typename SizeType>
+struct is_bounds<strided_bounds<Rank, SizeType>> : std::integral_constant<bool, true> {};
+
+template <typename IndexType>
+class bounds_iterator
+ : public std::iterator<std::random_access_iterator_tag,
+ IndexType,
+ ptrdiff_t,
+ const details::arrow_proxy<IndexType>,
+ const IndexType>
+{
+private:
+ using Base = std::iterator <std::random_access_iterator_tag, IndexType, ptrdiff_t, const details::arrow_proxy<IndexType>, 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 <typename Bounds>
+ explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) _NOEXCEPT
+ : boundary(bnd.index_bounds())
+ , curr( std::move(curr) )
+ {
+ static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
+ }
+ reference operator*() const _NOEXCEPT
+ {
+ return curr;
+ }
+ pointer operator->() const _NOEXCEPT
+ {
+ return details::arrow_proxy<value_type>{ 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 <typename SizeType>
+class bounds_iterator<index<1, SizeType>>
+ : public std::iterator<std::random_access_iterator_tag,
+ index<1, SizeType>,
+ ptrdiff_t,
+ const details::arrow_proxy<index<1, SizeType>>,
+ const index<1, SizeType>>
+{
+ using Base = std::iterator<std::random_access_iterator_tag, index<1, SizeType>, ptrdiff_t, const details::arrow_proxy<index<1, SizeType>>, 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 <typename Bounds>
+ 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<value_type>{ 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 <typename IndexType>
+bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n, const bounds_iterator<IndexType>& rhs) _NOEXCEPT
+{
+ return rhs + n;
+}
+
+/*
+** begin definitions of basic_array_view
+*/
+namespace details
+{
+ template <typename Bounds>
+ _CONSTEXPR std::enable_if_t<std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT
+ {
+ return bnd.strides;
+ }
+
+ // Make a stride vector from bounds, assuming continugous memory.
+ template <typename Bounds>
+ _CONSTEXPR std::enable_if_t<std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::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 <typename BoundsSrc, typename BoundsDest>
+ void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest)
+ {
+ static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value, "The src type and dest type must be bounds");
+ static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::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 <typename ArrayView>
+class contiguous_array_view_iterator;
+template <typename ArrayView>
+class general_array_view_iterator;
+enum class byte : std::uint8_t {};
+
+template <typename ValueType, typename BoundsType>
+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<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view>, general_array_view_iterator<basic_array_view>>;
+ using const_iterator = std::conditional_t<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view<const ValueType, BoundsType>>, general_array_view_iterator<basic_array_view<const ValueType, BoundsType>>>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ using sliced_type = std::conditional_t<rank == 1, value_type, basic_array_view<value_type, typename BoundsType::sliced_type>>;
+
+private:
+ pointer m_pdata;
+ bounds_type m_bounds;
+
+public:
+ _CONSTEXPR bounds_type bounds() const _NOEXCEPT
+ {
+ return m_bounds;
+ }
+ template <unsigned int Dim = 0>
+ _CONSTEXPR size_type extent() const _NOEXCEPT
+ {
+ return m_bounds.template extent<Dim>();
+ }
+ _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 <bool Enabled = rank != 1, typename Ret = std::enable_if_t<Enabled, sliced_type>>
+ _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<const basic_array_view<const value_type, bounds_type> *>(this), true};
+ }
+ _CONSTEXPR const_iterator cend() const
+ {
+ return const_iterator {reinterpret_cast<const basic_array_view<const value_type, bounds_type> *>(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 <typename OtherValueType, typename OtherBoundsType, typename Dummy = std::enable_if_t<std::is_same<std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
+ _CONSTEXPR auto operator == (const basic_array_view<OtherValueType, OtherBoundsType> & 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 <typename OtherValueType, typename OtherBounds,
+ typename Dummy = std::enable_if_t<std::is_convertible<OtherValueType(*)[], value_type(*)[]>::value
+ && std::is_convertible<OtherBounds, bounds_type>::value>>
+ _CONSTEXPR basic_array_view(const basic_array_view<OtherValueType, OtherBounds> & 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 <typename T>
+ _CONSTEXPR basic_array_view(T *data, std::enable_if_t<std::is_same<value_type, std::remove_all_extents_t<T>>::value, bounds_type> bound) _NOEXCEPT
+ : m_pdata(reinterpret_cast<pointer>(data))
+ , m_bounds(std::move(bound))
+ {
+ fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0);
+ }
+ template <typename DestBounds>
+ _CONSTEXPR basic_array_view<value_type, DestBounds> as_array_view(const DestBounds &bounds)
+ {
+ details::verifyBoundsReshape(m_bounds, bounds);
+ return {m_pdata, bounds};
+ }
+private:
+
+ friend iterator;
+ friend const_iterator;
+ template <typename ValueType2, typename BoundsType2>
+ friend class basic_array_view;
+};
+
+template <size_t DimSize = dynamic_range>
+struct dim
+{
+ static const size_t value = DimSize;
+};
+template <>
+struct dim<dynamic_range>
+{
+ static const size_t value = dynamic_range;
+ const size_t dvalue;
+ dim(size_t size) : dvalue(size) {}
+};
+
+template <typename ValueTypeOpt, size_t FirstDimension = dynamic_range, size_t... RestDimensions>
+class array_view;
+template <typename ValueTypeOpt, unsigned int Rank>
+class strided_array_view;
+
+namespace details
+{
+ template <typename T, typename = std::true_type>
+ struct ArrayViewTypeTraits
+ {
+ using value_type = T;
+ using size_type = size_t;
+ };
+
+ template <typename Traits>
+ struct ArrayViewTypeTraits<Traits, typename std::is_reference<typename Traits::array_view_traits &>::type>
+ {
+ using value_type = typename Traits::array_view_traits::value_type;
+ using size_type = typename Traits::array_view_traits::size_type;
+ };
+
+ template <typename T, typename SizeType, size_t... Ranks>
+ struct ArrayViewArrayTraits {
+ using type = array_view<T, Ranks...>;
+ using value_type = T;
+ using bounds_type = static_bounds<SizeType, Ranks...>;
+ using pointer = T*;
+ using reference = T&;
+ };
+ template <typename T, typename SizeType, size_t N, size_t... Ranks>
+ struct ArrayViewArrayTraits<T[N], SizeType, Ranks...> : ArrayViewArrayTraits<T, SizeType, Ranks..., N> {};
+
+ template <typename BoundsType>
+ BoundsType newBoundsHelperImpl(size_t totalSize, std::true_type) // dynamic size
+ {
+ fail_fast_assert(totalSize <= details::SizeTypeTraits<typename BoundsType::size_type>::max_value);
+ return BoundsType{static_cast<typename BoundsType::size_type>(totalSize)};
+ }
+ template <typename BoundsType>
+ BoundsType newBoundsHelperImpl(size_t totalSize, std::false_type) // static size
+ {
+ fail_fast_assert(BoundsType::static_size == totalSize);
+ return {};
+ }
+ template <typename BoundsType>
+ BoundsType newBoundsHelper(size_t totalSize)
+ {
+ static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1");
+ return newBoundsHelperImpl<BoundsType>(totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>());
+ }
+
+ struct Sep{};
+
+ template <typename T, typename... Args>
+ T static_as_array_view_helper(Sep, Args... args)
+ {
+ return T{static_cast<typename T::size_type>(args)...};
+ }
+ template <typename T, typename Arg, typename... Args>
+ std::enable_if_t<!std::is_same<Arg, dim<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T> static_as_array_view_helper(Arg, Args... args)
+ {
+ return static_as_array_view_helper<T>(args...);
+ }
+ template <typename T, typename... Args>
+ T static_as_array_view_helper(dim<dynamic_range> val, Args ... args)
+ {
+ return static_as_array_view_helper<T>(args..., val.dvalue);
+ }
+
+ template <typename SizeType, typename ...Dimensions>
+ struct static_as_array_view_static_bounds_helper
+ {
+ using type = static_bounds<SizeType, (Dimensions::value)...>;
+ };
+
+ template <typename T>
+ struct is_array_view_oracle : std::false_type
+ {};
+ template <typename ValueType, size_t FirstDimension, size_t... RestDimensions>
+ struct is_array_view_oracle<array_view<ValueType, FirstDimension, RestDimensions...>> : std::true_type
+ {};
+ template <typename ValueType, unsigned int Rank>
+ struct is_array_view_oracle<strided_array_view<ValueType, Rank>> : std::true_type
+ {};
+ template <typename T>
+ struct is_array_view : is_array_view_oracle<std::remove_cv_t<T>>
+ {};
+
+}
+
+
+template <typename ValueType, typename SizeType>
+struct array_view_options
+{
+ struct array_view_traits
+ {
+ using value_type = ValueType;
+ using size_type = SizeType;
+ };
+};
+
+template <typename ValueTypeOpt, size_t FirstDimension, size_t... RestDimensions>
+class array_view : public basic_array_view<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type,
+ static_bounds<typename details::ArrayViewTypeTraits<ValueTypeOpt>::size_type, FirstDimension, RestDimensions...>>
+{
+ template <typename ValueTypeOpt2, size_t FirstDimension2, size_t... RestDimensions2>
+ friend class array_view;
+ using Base = basic_array_view<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type,
+ static_bounds<typename details::ArrayViewTypeTraits<ValueTypeOpt>::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 <size_t DynamicRank = bounds_type::dynamic_rank, typename Dummy = std::enable_if_t<DynamicRank != 0>>
+ _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 T, typename Helper = details::ArrayViewArrayTraits<T, size_type, dynamic_range>,
+ typename Dummy = std::enable_if_t<std::is_convertible<typename Helper::value_type (*)[], typename Base::value_type (*)[]>::value
+ && std::is_convertible<typename Helper::bounds_type, typename Base::bounds_type>::value>>
+ _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size})
+ {
+ }
+
+ // from n-dimensions static array
+ template <typename T, size_t N, typename Helper = details::ArrayViewArrayTraits<T, size_type, N>,
+ typename Dummy = std::enable_if_t<std::is_convertible<typename Helper::value_type(*)[], typename Base::value_type(*)[]>::value
+ && std::is_convertible<typename Helper::bounds_type, typename Base::bounds_type>::value>>
+ _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type())
+ {
+ }
+
+ // from n-dimensions static array with size
+ template <typename T, size_t N, typename Helper = details::ArrayViewArrayTraits<T, size_type, dynamic_range>,
+ typename Dummy = std::enable_if_t<std::is_convertible<typename Helper::value_type(*)[], typename Base::value_type(*)[]>::value
+ && std::is_convertible<typename Helper::bounds_type, typename Base::bounds_type>::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 <size_t N, typename Dummy = std::enable_if_t<std::is_convertible<static_bounds<size_type, N>, typename Base::bounds_type>::value>>
+ _CONSTEXPR array_view (std::array<std::remove_const_t<value_type>, N> & arr) : Base(arr.data(), static_bounds<size_type, N>())
+ {
+ }
+
+ template <size_t N, typename Dummy = std::enable_if_t<std::is_convertible<static_bounds<size_type, N>, typename Base::bounds_type>::value && std::is_const<value_type>::value>>
+ _CONSTEXPR array_view (const std::array<std::remove_const_t<value_type>, N> & arr) : Base(arr.data(), static_bounds<size_type, N>())
+ {
+ }
+
+
+ // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity
+ template <typename Ptr,
+ typename Dummy = std::enable_if_t<std::is_convertible<Ptr, pointer>::value
+ && details::LessThan<Base::bounds_type::dynamic_rank, 2>::value>> // remove literal 0 case
+ _CONSTEXPR array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper<typename Base::bounds_type>(static_cast<pointer>(end) - begin))
+ {
+ }
+
+ // from containers. It must has .size() and .data() two function signatures
+ template <typename Cont, typename DataType = typename Cont::value_type, typename SizeType = typename Cont::size_type,
+ typename Dummy = std::enable_if_t<!details::is_array_view<Cont>::value
+ && std::is_convertible<DataType (*)[], typename Base::value_type (*)[]>::value
+ && std::is_convertible<static_bounds<SizeType, dynamic_range>, typename Base::bounds_type>::value
+ && std::is_same<std::decay_t<decltype(std::declval<Cont>().size(), *std::declval<Cont>().data())>, DataType>::value>
+ >
+ _CONSTEXPR array_view (Cont& cont) : Base(static_cast<pointer>(cont.data()), details::newBoundsHelper<typename Base::bounds_type>(cont.size()))
+ {
+
+ }
+
+ _CONSTEXPR array_view(const array_view &) = default;
+
+ // convertible
+ template <typename OtherValueTypeOpt, size_t... OtherDimensions,
+ typename BaseType = basic_array_view<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type, static_bounds<typename details::ArrayViewTypeTraits<ValueTypeOpt>::size_type, FirstDimension, RestDimensions...>>,
+ typename OtherBaseType = basic_array_view<typename details::ArrayViewTypeTraits<OtherValueTypeOpt>::value_type, static_bounds<typename details::ArrayViewTypeTraits<OtherValueTypeOpt>::size_type, OtherDimensions...>>,
+ typename Dummy = std::enable_if_t<std::is_convertible<OtherBaseType, BaseType>::value>
+ >
+ _CONSTEXPR array_view(const array_view<OtherValueTypeOpt, OtherDimensions...> &av) : Base(static_cast<const typename array_view<OtherValueTypeOpt, OtherDimensions...>::Base &>(av)) {} // static_cast is required
+
+ // reshape
+ template <typename... Dimensions2>
+ _CONSTEXPR array_view<ValueTypeOpt, Dimensions2::value...> 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<ValueTypeOpt, (Dimensions2::value)...>::bounds_type;
+ auto tobounds = details::static_as_array_view_helper<BoundsType>(dims..., details::Sep{});
+ details::verifyBoundsReshape(this->bounds(), tobounds);
+ return {this->data(), tobounds};
+ }
+
+ // to bytes array
+ template <bool Enabled = std::is_standard_layout<std::decay_t<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type>>::value>
+ _CONSTEXPR auto as_bytes() const _NOEXCEPT ->
+ array_view<array_view_options<const byte, size_type>, static_cast<size_t>(details::StaticSizeHelper<size_type, Base::bounds_type::static_size, sizeof(value_type)>::value)>
+ {
+ static_assert(Enabled, "The value_type of array_view must be standarded layout");
+ return { reinterpret_cast<const byte*>(this->data()), this->bytes() };
+ }
+
+ template <bool Enabled = std::is_standard_layout<std::decay_t<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type>>::value>
+ _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT ->
+ array_view<array_view_options<byte, size_type>, static_cast<size_t>(details::StaticSizeHelper<size_type, Base::bounds_type::static_size, sizeof(value_type)>::value)>
+ {
+ static_assert(Enabled, "The value_type of array_view must be standarded layout");
+ return { reinterpret_cast<byte*>(this->data()), this->bytes() };
+ }
+
+
+ // from bytes array
+ template<typename U, bool IsByte = std::is_same<value_type, const byte>::value, typename Dummy = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>>
+ _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view<const U, (Base::bounds_type::dynamic_rank == 0 ? Base::bounds_type::static_size / sizeof(U) : static_cast<size_type>(dynamic_range))>
+ {
+ static_assert(std::is_standard_layout<U>::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<const U*>(this->data()), this->bytes() / sizeof(U) };
+ }
+
+ template<typename U, bool IsByte = std::is_same<value_type, byte>::value, typename Dummy = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>>
+ _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view<U, (Base::bounds_type::dynamic_rank == 0 ? Base::bounds_type::static_size / sizeof(U) : static_cast<size_type>(dynamic_range))>
+ {
+ static_assert(std::is_standard_layout<U>::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<U*>(this->data()), this->bytes() / sizeof(U) };
+ }
+
+ // section on linear space
+ template<size_t Count>
+ _CONSTEXPR array_view<ValueTypeOpt, Count> 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<ValueTypeOpt, dynamic_range> first(size_type count) const _NOEXCEPT
+ {
+ fail_fast_assert(count <= this->size());
+ return { this->data(), count };
+ }
+
+ template<size_t Count>
+ _CONSTEXPR array_view<ValueTypeOpt, Count> 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<ValueTypeOpt, dynamic_range> last(size_type count) const _NOEXCEPT
+ {
+ fail_fast_assert(count <= this->size());
+ return { this->data() + this->size() - count, count };
+ }
+
+ template<size_t Offset, size_t Count>
+ _CONSTEXPR array_view<ValueTypeOpt, Count> 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<ValueTypeOpt, dynamic_range> 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<ValueTypeOpt, rank> section(index_type origin, index_type extents) const
+ {
+ return { &this->operator[](origin), strided_bounds<rank, size_type> {extents, details::make_stride(Base::bounds())}};
+ }
+};
+
+template <typename T, size_t... Dimensions>
+_CONSTEXPR auto as_array_view(T * const & ptr, dim<Dimensions>... args) -> array_view<std::remove_all_extents_t<T>, Dimensions...>
+{
+ return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr), details::static_as_array_view_helper<static_bounds<size_t, Dimensions...>>(args..., details::Sep{})};
+}
+
+template <typename T>
+_CONSTEXPR auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits<T, size_t, dynamic_range>::type
+{
+ return {arr, len};
+}
+
+template <typename T, size_t N>
+_CONSTEXPR auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits<T, size_t, N>::type
+{
+ return {arr};
+}
+
+template <typename T, size_t N>
+_CONSTEXPR array_view<const T, N> as_array_view(const std::array<T, N> &arr)
+{
+ return {arr};
+}
+
+template <typename T, size_t N>
+_CONSTEXPR array_view<const T, N> as_array_view(const std::array<T, N> &&) = delete;
+
+template <typename T, size_t N>
+_CONSTEXPR array_view<T, N> as_array_view(std::array<T, N> &arr)
+{
+ return {arr};
+}
+
+template <typename T>
+_CONSTEXPR array_view<T, dynamic_range> as_array_view(T *begin, T *end)
+{
+ return {begin, end};
+}
+
+template <typename Cont>
+_CONSTEXPR auto as_array_view(Cont &arr) -> std::enable_if_t<!details::is_array_view<std::decay_t<Cont>>::value,
+ array_view<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
+{
+ return {arr.data(), arr.size()};
+}
+
+template <typename Cont>
+_CONSTEXPR auto as_array_view(Cont &&arr) -> std::enable_if_t<!details::is_array_view<std::decay_t<Cont>>::value,
+ array_view<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
+
+template <typename ValueTypeOpt, unsigned int Rank>
+class strided_array_view : public basic_array_view<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type, strided_bounds<Rank, typename details::ArrayViewTypeTraits<ValueTypeOpt>::size_type>>
+{
+ using Base = basic_array_view<typename details::ArrayViewTypeTraits<ValueTypeOpt>::value_type, strided_bounds<Rank, typename details::ArrayViewTypeTraits<ValueTypeOpt>::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 <size_t... Dimensions, typename Dummy = std::enable_if<sizeof...(Dimensions) == Rank>>
+ strided_array_view (array_view<ValueTypeOpt, Dimensions...> 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 <typename ArrayView>
+class contiguous_array_view_iterator : public std::iterator<std::random_access_iterator_tag, typename ArrayView::value_type>
+{
+ using Base = std::iterator<std::random_access_iterator_tag, typename ArrayView::value_type>;
+public:
+ using typename Base::reference;
+ using typename Base::pointer;
+ using typename Base::difference_type;
+private:
+ template <typename ValueType, typename Bounds>
+ 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 <typename ArrayView>
+contiguous_array_view_iterator<ArrayView> operator+(typename contiguous_array_view_iterator<ArrayView>::difference_type n, const contiguous_array_view_iterator<ArrayView>& rhs) _NOEXCEPT
+{
+ return rhs + n;
+}
+
+template <typename ArrayView>
+class general_array_view_iterator : public std::iterator<std::random_access_iterator_tag, typename ArrayView::value_type>
+{
+ using Base = std::iterator<std::random_access_iterator_tag, typename ArrayView::value_type>;
+public:
+ using typename Base::reference;
+ using typename Base::pointer;
+ using typename Base::difference_type;
+ using typename Base::value_type;
+private:
+ template <typename ValueType, typename Bounds>
+ 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 <typename ArrayView>
+general_array_view_iterator<ArrayView> operator+(typename general_array_view_iterator<ArrayView>::difference_type n, const general_array_view_iterator<ArrayView>& 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 <exception>
+
+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 <memory>
+
+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 F>
+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 <class F>
+Final_act<F> finally(F f) { return Final_act<F>(f); }
+
+// narrow_cast(): a searchable way to do narrowing casts of values
+template<class T, class U>
+T narrow_cast(U u) { return static_cast<T>(u); }
+
+struct narrowing_error : public std::exception {};
+// narrow() : a checked version of narrow_cast() that throws if the cast changed the value
+template<class T, class U>
+T narrow(U u) { T t = narrow_cast<T>(u); if (static_cast<U>(t) != u) throw narrowing_error(); return t; }
+
+//
+// at() - Bounds-checked way of accessing static arrays, std::array, std::vector
+//
+template <class T, size_t N>
+T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; }
+
+template <class T, size_t N>
+T& at(std::array<T, N>& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; }
+
+template <class Cont>
+typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; }
+
+
+//
+// not_null
+//
+// 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 T>
+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 <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ not_null(const not_null<U> &other) : ptr_(other.get())
+ {
+ }
+
+ not_null<T>& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; }
+
+ // prevents compilation when someone attempts to assign a nullptr
+ not_null<T>& operator=(std::nullptr_t) = delete;
+ not_null<T>& 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<T>& operator++() = delete;
+ not_null<T>& operator--() = delete;
+ not_null<T> operator++(int) = delete;
+ not_null<T> operator--(int) = delete;
+ not_null<T>& operator+(size_t) = delete;
+ not_null<T>& operator+=(size_t) = delete;
+ not_null<T>& operator-(size_t) = delete;
+ not_null<T>& operator-=(size_t) = delete;
+};
+
+
+//
+// maybe_null
+//
+// Describes an optional pointer - provides symmetry with not_null
+//
+template<class T>
+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 <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ maybe_null_dbg(const not_null<U> &other) : ptr_(other.get()), tested_(false)
+ {
+ }
+
+ template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ maybe_null_dbg(const maybe_null_dbg<U> &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<T>& operator++() = delete;
+ maybe_null_dbg<T>& operator--() = delete;
+ maybe_null_dbg<T> operator++(int) = delete;
+ maybe_null_dbg<T> operator--(int) = delete;
+ maybe_null_dbg<T>& operator+(size_t) = delete;
+ maybe_null_dbg<T>& operator+=(size_t) = delete;
+ maybe_null_dbg<T>& operator-(size_t) = delete;
+ maybe_null_dbg<T>& operator-=(size_t) = delete;
+
+ T ptr_;
+ mutable bool tested_;
+};
+
+template<class T>
+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 <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ maybe_null_ret(const not_null<U> &other) : ptr_(other.get())
+ {
+ }
+
+ template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ maybe_null_ret(const maybe_null_ret<U> &other) : ptr_(other.get())
+ {
+ }
+
+ template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
+ maybe_null_ret(const maybe_null_dbg<U> &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<T>& operator++() = delete;
+ maybe_null_ret<T>& operator--() = delete;
+ maybe_null_ret<T> operator++(int) = delete;
+ maybe_null_ret<T> operator--(int) = delete;
+ maybe_null_ret<T>& operator+(size_t) = delete;
+ maybe_null_ret<T>& operator+=(size_t) = delete;
+ maybe_null_ret<T>& operator-(size_t) = delete;
+ maybe_null_ret<T>& operator-=(size_t) = delete;
+
+ const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type
+ T ptr_;
+};
+
+template<class T> using maybe_null = maybe_null_ret<T>;
+
+} // 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 <cstring>
+
+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<size_t Max = dynamic_range>
+using czstring = const char*;
+
+template<size_t Max = dynamic_range>
+using cwzstring = const wchar_t*;
+
+template<size_t Max = dynamic_range>
+using zstring = char*;
+
+template<size_t Max = dynamic_range>
+using wzstring = wchar_t*;
+
+//
+// string_view and relatives
+//
+// Note that Extent is always single-dimension only
+// Note that SizeType is defaulted to be smaller than size_t which is the array_view default
+//
+// TODO (neilmac) once array_view regains configurable size_type, update these typedef's
+//
+template <class CharT, size_t Extent = dynamic_range>
+using basic_string_view = array_view<CharT, Extent>;
+
+template<size_t Extent = dynamic_range>
+using string_view = basic_string_view<char, Extent>;
+
+template<size_t Extent = dynamic_range>
+using cstring_view = basic_string_view<const char, Extent>;
+
+template<size_t Extent = dynamic_range>
+using wstring_view = basic_string_view<wchar_t, Extent>;
+
+template<size_t Extent = dynamic_range>
+using cwstring_view = basic_string_view<const wchar_t, Extent>;
+
+
+//
+// ensure_sentinel()
+//
+// Provides a way to obtain an array_view from a contiguous sequence
+// that ends with a (non-inclusive) sentinel value.
+//
+// Will fail-fast if sentinel cannot be found before max elements are examined.
+//
+template<class T, class SizeType, const T Sentinel>
+array_view<T, dynamic_range> ensure_sentinel(const T* seq, SizeType max = std::numeric_limits<SizeType>::max())
+{
+ auto cur = seq;
+ while ((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<class T>
+inline basic_string_view<T, dynamic_range> ensure_z(T* const & sz, size_t max = std::numeric_limits<size_t>::max())
+{
+ return ensure_sentinel<0>(sz, max);
+}
+
+// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation
+inline basic_string_view<char, dynamic_range> ensure_z(char* const & sz, size_t max)
+{
+ auto len = strnlen(sz, max);
+ fail_fast_assert(sz[len] == 0); return{ sz, len };
+}
+
+inline basic_string_view<const char, dynamic_range> ensure_z(const char* const& sz, size_t max)
+{
+ auto len = strnlen(sz, max);
+ fail_fast_assert(sz[len] == 0); return{ sz, len };
+}
+
+inline basic_string_view<wchar_t, dynamic_range> ensure_z(wchar_t* const & sz, size_t max)
+{
+ auto len = wcsnlen(sz, max);
+ fail_fast_assert(sz[len] == 0); return{ sz, len };
+}
+
+inline basic_string_view<const wchar_t, dynamic_range> ensure_z(const wchar_t* const & sz, size_t max)
+{
+ auto len = wcsnlen(sz, max);
+ fail_fast_assert(sz[len] == 0); return{ sz, len };
+}
+
+template<class T, size_t N>
+basic_string_view<T, dynamic_range> ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], N); }
+
+template<class Cont>
+basic_string_view<typename std::remove_pointer<typename Cont::pointer>::type, dynamic_range> ensure_z(Cont& cont)
+{
+ return ensure_z(cont.data(), cont.length());
+}
+
+//
+// to_string() allow (explicit) conversions from string_view to string
+//
+template<class CharT, size_t Extent>
+std::basic_string<typename std::remove_const<CharT>::type> to_string(const basic_string_view<CharT, Extent>& view)
+{
+ return{ view.data(), view.length() };
+}
+
+
+template<class CharT, size_t Extent = dynamic_range>
+class basic_zstring_builder
+{
+public:
+ using string_view_type = basic_string_view<CharT, Extent>;
+ using value_type = CharT;
+ using pointer = CharT*;
+ using size_type = typename string_view_type::size_type;
+ using iterator = typename string_view_type::iterator;
+
+ basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {}
+
+ template<size_t Size>
+ basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {}
+
+ pointer data() const { return sv_.data(); }
+ string_view_type view() const { return sv_; }
+
+ size_type length() const { return sv_.length(); }
+
+ pointer assume0() const { return data(); }
+ string_view_type ensure_z() const { return Guide::ensure_z(sv_); }
+
+ iterator begin() const { return sv_.begin(); }
+ iterator end() const { return sv_.end(); }
+
+private:
+ string_view_type sv_;
+};
+
+template <size_t Max = dynamic_range>
+using zstring_builder = basic_zstring_builder<char, Max>;
+
+template <size_t Max = dynamic_range>
+using wzstring_builder = basic_zstring_builder<wchar_t, Max>;
+}
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 <UnitTest++/UnitTest++.h>
+#include <array_view.h>
+#include <numeric>
+#include <array>
+#include <string>
+#include <vector>
+#include <list>
+#include <iostream>
+#include <functional>
+#include <algorithm>
+
+
+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<size_t, 4, dynamic_range, 2> bounds{ 3 };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ array_view<int, 4, dynamic_range, 2> av(nullptr, bounds);
+ av.extent();
+ av.extent<2>();
+ av[8][4][3];
+#endif
+ }
+
+ TEST (array_view_convertible)
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ array_view<int, 7, 4, 2> av1(nullptr, b1);
+#endif
+
+ auto f = [&]() { array_view<int, 7, 4, 2> av1(nullptr); };
+ CHECK_THROW(f(), fail_fast);
+
+ array_view<int, 7, dynamic_range, 2> av1(nullptr);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ static_bounds<size_t, 7, dynamic_range, 2> b12(b11);
+ b12 = b11;
+ b11 = b12;
+
+ array_view<int, dynamic_range> av1 = nullptr;
+ array_view<int, 7, dynamic_range, 2> av2(av1);
+ array_view<int, 7, 4, 2> av2(av1);
+#endif
+
+ array_view<DerivedClass> avd;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ array_view<BaseClass> avb = avd;
+#endif
+ array_view<const DerivedClass> 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<const int, dynamic_range, 3, 5> exp, int expected_value) {
+ for (auto val : exp)
+ {
+ CHECK(val == expected_value);
+ }
+ }
+
+ void overloaded_func(array_view<const char, dynamic_range, 3, 5> exp, char expected_value) {
+ for (auto val : exp)
+ {
+ CHECK(val == expected_value);
+ }
+ }
+
+ void fixed_func(array_view<int, 3, 3, 5> 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<int, 15> 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<double, 3> arr = {0.0, 0.0, 0.0};
+ auto cv = as_array_view(arr);
+
+ vector<float> 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<int>();
+
+ 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<Foo>();
+#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<int, dynamic_range> av(nullptr);
+ CHECK(av.length() == 0);
+
+ array_view<int, dynamic_range> av2;
+ CHECK(av2.length() == 0);
+
+ array_view<int, dynamic_range> av3(nullptr, 0);
+ CHECK(av3.length() == 0);
+
+ // Constructing from a nullptr + length is specifically disallowed
+ auto f = [&]() {array_view<int, dynamic_range> av4(nullptr, 2);};
+ CHECK_THROW(f(), fail_fast);
+
+ int arr1[2][3];
+ array_view<int, 2, dynamic_range> av5(arr1);
+
+ array<int, 15> arr2;
+ array_view<int, 15> av6(arr2);
+
+ vector<int> vec1(19);
+ array_view<int> av7(vec1);
+ CHECK(av7.length() == 19);
+
+
+ array_view<int> av8;
+ CHECK(av8.length() == 0);
+ array_view<int> av9(arr2);
+ CHECK(av9.length() == 15);
+
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ array_view<int, 4> av10;
+ DerivedClass *p = nullptr;
+ array_view<BaseClass> av11(p, 0);
+#endif
+
+
+ }
+
+ TEST(copyandassignment)
+ {
+ array_view<int, dynamic_range> av1;
+
+ int arr[] = {3, 4, 5};
+ av1 = arr;
+ array_view<array_view_options<const int, unsigned char>, dynamic_range> av2;
+ av2 = av1;
+ }
+
+ TEST(array_view_first)
+ {
+ int arr[5] = { 1, 2, 3, 4, 5 };
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.first<2>().bounds() == static_bounds<size_t, 2>()));
+ CHECK(av.first<2>().length() == 2);
+ CHECK(av.first(2).length() == 2);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.first<0>().bounds() == static_bounds<size_t, 0>()));
+ CHECK(av.first<0>().length() == 0);
+ CHECK(av.first(0).length() == 0);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.first<5>().bounds() == static_bounds<size_t, 5>()));
+ CHECK(av.first<5>().length() == 5);
+ CHECK(av.first(5).length() == 5);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK(av.first<6>().bounds() == static_bounds<size_t, 6>());
+ CHECK(av.first<6>().length() == 6);
+#endif
+ CHECK_THROW(av.first(6).length(), fail_fast);
+ }
+
+ {
+ array_view<int, dynamic_range> av;
+ CHECK((av.first<0>().bounds() == static_bounds<size_t, 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<int, 5> av = arr;
+ CHECK((av.last<2>().bounds() == static_bounds<size_t, 2>()));
+ CHECK(av.last<2>().length() == 2);
+ CHECK(av.last(2).length() == 2);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.last<0>().bounds() == static_bounds<size_t, 0>()));
+ CHECK(av.last<0>().length() == 0);
+ CHECK(av.last(0).length() == 0);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.last<5>().bounds() == static_bounds<size_t, 5>()));
+ CHECK(av.last<5>().length() == 5);
+ CHECK(av.last(5).length() == 5);
+ }
+
+
+ {
+ array_view<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK((av.last<6>().bounds() == static_bounds<size_t, 6>()));
+ CHECK(av.last<6>().length() == 6);
+#endif
+ CHECK_THROW(av.last(6).length(), fail_fast);
+ }
+
+ {
+ array_view<int, dynamic_range> av;
+ CHECK((av.last<0>().bounds() == static_bounds<size_t, 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<array_view_options<double, char>, 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<const double, dynamic_range, 6, 4> 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<int, 5> av = arr;
+ CHECK((av.sub<2,2>().bounds() == static_bounds<size_t, 2>()));
+ CHECK((av.sub<2,2>().length() == 2));
+ CHECK(av.sub(2,2).length() == 2);
+ }
+
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.sub<0,0>().bounds() == static_bounds<size_t, 0>()));
+ CHECK((av.sub<0,0>().length() == 0));
+ CHECK(av.sub(0,0).length() == 0);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+ CHECK((av.sub<0,5>().bounds() == static_bounds<size_t, 5>()));
+ CHECK((av.sub<0,5>().length() == 5));
+ CHECK(av.sub(0,5).length() == 5);
+ }
+
+ {
+ array_view<int, 5> av = arr;
+#ifdef CONFIRM_COMPILATION_ERRORS
+ CHECK((av.sub<5,0>().bounds() == static_bounds<size_t, 0>()));
+ CHECK((av.sub<5,0>().length() == 0));
+#endif
+ CHECK_THROW(av.sub(5,0).length(), fail_fast);
+ }
+
+ {
+ array_view<int, dynamic_range> av;
+ CHECK((av.sub<0,0>().bounds() == static_bounds<size_t, 0>()));
+ CHECK((av.sub<0,0>().length() == 0));
+ CHECK(av.sub(0,0).length() == 0);
+ }
+ }
+
+ void AssertNullEmptyProperties(array_view<int, dynamic_range>& av)
+ {
+ CHECK(av.length() == 0);
+ CHECK(av.data() == nullptr);
+ CHECK(!av);
+ }
+
+ template <class T, class U>
+ 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<int, dynamic_range> av;
+ AssertNullEmptyProperties(av);
+
+ array_view<int, dynamic_range> av2(nullptr);
+ AssertNullEmptyProperties(av2);
+ }
+
+ TEST(ArrayConstruction)
+ {
+ int a[] = { 1, 2, 3, 4 };
+
+ array_view<int, dynamic_range> av = { &a[1], 3 };
+ CHECK(av.length() == 3);
+
+ array_view<int, dynamic_range> av3 = { a, 2 };
+ CHECK(av3.length() == 2);
+
+ array_view<int, dynamic_range> av2 = a;
+ CHECK(av2.length() == 4);
+ }
+
+ TEST(NonConstConstConversions)
+ {
+ int a[] = { 1, 2, 3, 4 };
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ array_view<const int, dynamic_range> cav = a;
+ array_view<int, dynamic_range> av = cav;
+#else
+ array_view<int, dynamic_range> av = a;
+ array_view<const int, dynamic_range> 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<int, 4> av4 = arr;
+ CHECK(av4.length() == 4);
+
+ // converting to dynamic_range a_v is always ok
+ {
+ array_view<int, dynamic_range> av = av4;
+ }
+ {
+ array_view<int, dynamic_range> av = arr;
+ }
+
+ // initialization or assignment to static array_view that REDUCES size is NOT ok
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ array_view<int, 2> av2 = arr;
+ }
+ {
+ array_view<int, 2> av2 = av4;
+ }
+#endif
+
+ {
+ array_view<int, dynamic_range> av = arr;
+ array_view<int, 2> av2 = av;
+ }
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ {
+ array_view<int, dynamic_range> av = arr;
+ array_view<int, 2, 1> av2 = av.as_array_view(dim<2>(), dim<2>());
+ }
+#endif
+
+ {
+ array_view<int, dynamic_range> av = arr;
+ auto f = [&]() {array_view<int, 2, 1> 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<int, 2> av2 = {arr, 2};
+ }
+ {
+ array_view<int, 1> av2 = av4.first<1>();
+ }
+
+ // ...or dynamically
+ {
+ // NB: implicit conversion to array_view<int,2> from array_view<int,dynamic_range>
+ array_view<int, 1> 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<int, 4> av4 = arr2;
+ }
+ {
+ array_view<int, 2> av2 = arr2;
+ array_view<int, 4> av4 = av2;
+ }
+#endif
+ {
+ auto f = [&]() {array_view<int, 4> 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<int, dynamic_range> av = arr2;
+ auto f = [&](){ array_view<int, 4> 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<const int, dynamic_range> av = a;
+ auto wav = av.as_writeable_bytes();
+#endif
+ }
+
+ {
+ array_view<int, dynamic_range> av;
+ auto wav = av.as_writeable_bytes();
+ CHECK(wav.length() == av.length());
+ CHECK(wav.length() == 0);
+ CHECK(wav.bytes() == 0);
+ }
+
+ {
+ array_view<int, dynamic_range> 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<const int, dynamic_range, 2> av2 = av1;
+
+ CHECK(av1 == av2);
+
+ array_view<array_view_options<int, char>, 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 <UnitTest++/UnitTest++.h>
+#include <gsl.h>
+
+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 <UnitTest++/UnitTest++.h>
+#include <gsl.h>
+#include <vector>
+
+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<int,4> 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<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);
+ }
+}
+
+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 <UnitTest++/UnitTest++.h>
+#include <array_view.h>
+#include <vector>
+
+using namespace std;
+using namespace Guide;;
+
+namespace
+{
+ void use(unsigned int&) {}
+}
+
+SUITE(bounds_test)
+{
+ TEST(basic_bounds)
+ {
+ for (auto point : static_bounds <unsigned int, dynamic_range, 3, 4 > { 2 })
+ {
+ for (unsigned int j = 0; j < decltype(point)::rank; j++)
+ {
+ use(j);
+ use(point[j]);
+ }
+ }
+ }
+
+ TEST(bounds_basic)
+ {
+ static_bounds<size_t, 3, 4, 5> b;
+ auto a = b.slice();
+ static_bounds<size_t, 4, dynamic_range, 2> x{ 4 };
+ x.slice().slice();
+ }
+
+ TEST (arrayview_iterator)
+ {
+ static_bounds<size_t, 4, dynamic_range, 2> 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<size_t, 7, 4, 2> b1;
+ static_bounds<size_t, 7, dynamic_range, 2> b2 = b1;
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ static_bounds<size_t, 7, dynamic_range, 1> b4 = b2;
+#endif
+
+ static_bounds<size_t, dynamic_range, dynamic_range, dynamic_range> b3 = b1;
+ static_bounds<int, 7, 4, 2> b4 = b3;
+
+ static_bounds<size_t, dynamic_range> b11;
+
+ static_bounds<size_t, dynamic_range> b5;
+ static_bounds<size_t, 34> b6;
+
+ b5 = static_bounds<size_t, 20>();
+ CHECK_THROW(b6 = b5, fail_fast);
+ b5 = static_bounds<size_t, 34>();
+ 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 <UnitTest++/UnitTest++.h>
+#include <gsl.h>
+
+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<int *> opt_n(&n);
+ int result = 0;
+ bool threw = false;
+
+ CHECK_THROW(result = *opt_n, fail_fast);
+ }
+
+ TEST(TestMaybeNull2)
+ {
+ int n = 5;
+ maybe_null<int *> opt_n(&n);
+ int result = 0;
+ if (opt_n.present())
+ result = *opt_n;
+ }
+
+ TEST(TestMaybeNull3)
+ {
+ int n = 5;
+ maybe_null<int *> opt_n(&n);
+ int result = 0;
+ if (opt_n != nullptr)
+ result = *opt_n;
+ }
+
+ int test4_helper(maybe_null<int *> 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<int *> 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<int> o(n);
+ }
+#endif
+
+ int g_int;
+ void test7_helper(maybe_null<maybe_null<int *> *> outptr)
+ {
+ g_int = 5;
+
+ if (outptr.present())
+ *outptr = &g_int;
+ }
+
+ void test7b_helper(maybe_null_dbg<maybe_null_dbg<int *> *> outptr)
+ {
+ g_int = 5;
+
+ if (outptr.present())
+ *outptr = &g_int;
+ }
+
+ TEST(TestMaybeNull7a)
+ {
+ maybe_null<int *> outval;
+ test7_helper(&outval);
+ CHECK(outval.present() && *outval == 5);
+ }
+
+ TEST(TestMaybeNull7b)
+ {
+ maybe_null_dbg<int *> outval;
+ test7b_helper(&outval);
+ CHECK_THROW((void)*outval, fail_fast);
+ }
+
+ int test8_helper1(maybe_null_dbg<int *> opt)
+ {
+ return *opt;
+ }
+
+ int test8_helper2a(maybe_null_dbg<int *> opt)
+ {
+ if (!opt.present())
+ return 0;
+ return test8_helper1(opt);
+ }
+
+ TEST(TestMaybeNull8a)
+ {
+ int n = 5;
+ maybe_null_dbg<int *> opt(&n);
+ CHECK_THROW(test8_helper2a(opt), fail_fast);
+ }
+
+#ifdef CONVERT_TO_PTR_TO_CONST
+ int test9_helper(maybe_null<const int *> copt)
+ {
+ if (copt.present())
+ return *copt;
+ return 0;
+ }
+
+ void TestMaybeNull9()
+ {
+ int n = 5;
+ maybe_null<int *> opt(&n);
+ CHECK_THROW(test9_helper(opt), fail_fast);
+ }
+#endif
+
+ TEST(TestMaybeNullCasting)
+ {
+ MyDerived derived;
+ maybe_null<MyDerived*> p = &derived;
+ CHECK(p.present());
+
+ maybe_null<MyBase*> q = p;
+ CHECK(q == p);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ maybe_null<Unrelated*> r = p;
+ maybe_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
+#endif
+ maybe_null_dbg<Unrelated*> t = reinterpret_cast<Unrelated*>(p.get());
+
+ CHECK_THROW((void)(void*)t.get(), fail_fast);
+ maybe_null_dbg<Unrelated*> u = reinterpret_cast<Unrelated*>(p.get());
+ CHECK(u.present());
+ CHECK((void*)p.get() == (void*)u.get());
+ }
+
+ TEST(TestMaybeNullArrow)
+ {
+ MyDerived derived;
+ maybe_null_dbg<MyDerived*> p = &derived;
+
+ CHECK_THROW(p->foo(), fail_fast);
+ CHECK(p.present());
+ CHECK(p->foo());
+
+ maybe_null<MyBase*> 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 <UnitTest++/UnitTest++.h>
+#include <gsl.h>
+
+using namespace Guide;
+
+struct MyBase {};
+struct MyDerived : public MyBase {};
+struct Unrelated {};
+
+// stand-in for a user-defined ref-counted class
+template<typename T>
+struct RefCounted
+{
+ RefCounted(T* p) : p_(p) {}
+ operator T*() { return p_; }
+ T* p_;
+};
+
+SUITE(NotNullTests)
+{
+
+ bool helper(not_null<int*> p)
+ {
+ return *p == 12;
+ }
+
+ TEST(TestNotNullConstructors)
+ {
+#ifdef CONFIRM_COMPILATION_ERRORS
+ not_null<int*> p = nullptr; // yay...does not compile!
+ not_null<std::vector<char>*> p = 0; // yay...does not compile!
+ not_null<int*> p; // yay...does not compile!
+ std::unique_ptr<int> up = std::make_unique<int>(120);
+ not_null<int*> p = up;
+#endif
+ int i = 12;
+ auto rp = RefCounted<int>(&i);
+ not_null<int*> p(rp);
+ CHECK(p.get() == &i);
+ }
+
+ TEST(TestNotNullCasting)
+ {
+ MyDerived derived;
+ not_null<MyDerived*> p = &derived;
+ not_null<MyBase*> q = p;
+ CHECK(q == p);
+
+#ifdef CONFIRM_COMPILATION_ERRORS
+ not_null<Unrelated*> r = p;
+ not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
+#endif
+ not_null<Unrelated*> t = reinterpret_cast<Unrelated*>(p.get());
+ CHECK((void*)p.get() == (void*)t.get());
+ }
+
+ TEST(TestNotNullAssignment)
+ {
+ int i = 12;
+ not_null<int*> 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 <UnitTest++/UnitTest++.h>
+#include <string_view.h>
+#include <vector>
+#include <cstdlib>
+
+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<char> 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 <UnitTest++/UnitTest++.h>
+#include <gsl.h>
+#include <functional>
+
+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<char>(n);
+ CHECK(c == 120);
+
+ n = 300;
+ unsigned char uc = narrow_cast<unsigned char>(n);
+ CHECK(uc == 44);
+ }
+
+ TEST(narrow)
+ {
+ int n = 120;
+ char c = narrow<char>(n);
+ CHECK(c == 120);
+
+ n = 300;
+ CHECK_THROW(narrow<char>(n), narrowing_error);
+ }
+}
+
+int main(int, const char *[])
+{
+ return UnitTest::RunAllTests();
+}