From a9dcbe04ff330ef8297191d19951d4a313b2115a Mon Sep 17 00:00:00 2001 From: Neil MacIntosh Date: Thu, 20 Aug 2015 18:09:14 -0700 Subject: [PATCH] Initial commit of library files. --- .gitignore | 1 + CMakeLists.txt | 11 + LICENSE | 11 + README.md | 64 +- include/array_view.h | 2113 +++++++++++++++++++++++++++++++++++ include/fail_fast.h | 39 + include/gsl.h | 284 +++++ include/string_view.h | 178 +++ tests/CMakeLists.txt | 143 +++ tests/array_view_tests.cpp | 648 +++++++++++ tests/assertion_tests.cpp | 53 + tests/at_tests.cpp | 60 + tests/bounds_tests.cpp | 99 ++ tests/maybenull_tests.cpp | 197 ++++ tests/notnull_tests.cpp | 87 ++ tests/string_view_tests.cpp | 104 ++ tests/utils_tests.cpp | 87 ++ 17 files changed, 4177 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 include/array_view.h create mode 100644 include/fail_fast.h create mode 100644 include/gsl.h create mode 100644 include/string_view.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/array_view_tests.cpp create mode 100644 tests/assertion_tests.cpp create mode 100644 tests/at_tests.cpp create mode 100644 tests/bounds_tests.cpp create mode 100644 tests/maybenull_tests.cpp create mode 100644 tests/notnull_tests.cpp create mode 100644 tests/string_view_tests.cpp create mode 100644 tests/utils_tests.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f5de3e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tests/unittest-cpp \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2125f7b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.2.2) + +project(GSL) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} +) + +enable_testing() + +add_subdirectory(tests) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..011f2f1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +Copyright (c) 2015 Microsoft Corporation. All rights reserved. + +This code is licensed under the MIT License (MIT). + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 6948aae..c0f100e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,62 @@ -# GSL -Guidelines Support Library +# GSL: Guidelines Support Library + +This library contains functions and types that are suggested for use by the +[C++ Coding Guidelines](https://github.com/Microsoft/CppCodingStandards/). + +These include types like `array_view<>`, `string_view<>`, `owner<>` and others. + +The entire implementation is provided inline in the headers under the [include](./include) directory. + +While some types have been broken out into their own headers (e.g. [include/array_view.h](./include/array_view.h)), +it is simplest to just include [gsl.h](./include/gsl.h) and gain access to the entire library. + +> NOTE: We encourage contributions that improve or refine any of the types in this library. + +# Quick Start +## Supported Platforms +The test suite that exercises GSL has been built and passes successfully on the following platforms: + +* Windows using Visual Studio 2013 +* Windows using Visual Studio 2015 +* Windows using Clang\LLVM 3.6 +* Windows using GCC 5.1 +* Linux using Clang\LLVM 3.6 +* Linux using GCC 5.1 + +> If you successfully port GSL to another platform, we would love to hear from you. Please consider contributing +any changes that were necessary back to this project to benefit the wider community. + +## Building the tests +To build the tests, you will require the following: + +* [CMake](http://cmake.org), version 3.3 or later to be installed and in your PATH. +* [UnitTest-cpp](https://github.com/Microsoft/unittest-cpp), to be cloned under the [tests/unittest-cpp](./tests/unittest-cpp) directory +of your GSL source. + +These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`. + +1. Create a directory to contain the build outputs for a particular architecture (we name it c:\GSL\vs14-x86 in this example). + + cd GSL + md build-x86 + cd build-x86 + +2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`). + + cmake -G "Visual Studio 14 2015" c:\GSL + +3. Build the test suite (in this case, in the Debug configuration, Release is another good choice). + + cmake --build . --config Debug + +4. Run the test suite. + + ctest -C Debug + +All tests should pass - indicating your platform is fully supported and you are ready to use the GSL types! + +## Using the libraries +As the types are entirely implemented inline in headers, there are no linking requirements. + +Just place the contents of the [include](./include) directory within your source tree so it is available +to your compiler, then include the appropriate headers in your program, and away you go! diff --git a/include/array_view.h b/include/array_view.h new file mode 100644 index 0000000..ecc3d1e --- /dev/null +++ b/include/array_view.h @@ -0,0 +1,2113 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fail_fast.h" + +#ifndef _MSC_VER +#define _CONSTEXPR constexpr +#else +#define _CONSTEXPR +#endif + +#pragma push_macro("_NOEXCEPT") + +#ifndef _NOEXCEPT + +#ifdef SAFER_CPP_TESTING +#define _NOEXCEPT +#else +#define _NOEXCEPT noexcept +#endif + +#else // _NOEXCEPT + +#ifdef SAFER_CPP_TESTING +#undef _NOEXCEPT +#define _NOEXCEPT +#endif + +#endif // _NOEXCEPT + +namespace Guide { + +/* +** begin definitions of index and bounds +*/ +namespace details +{ + template + struct SizeTypeTraits + { + static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + }; + + + template + class coordinate_facade + { + static_assert(std::is_integral::value + && sizeof(ValueType) <= sizeof(size_t), "ValueType must be unsigned integral type!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); + + template + friend class coordinate_facade; + public: + using reference = ValueType&; + using const_reference = const ValueType&; + using value_type = ValueType; + static const unsigned int rank = Rank; + _CONSTEXPR coordinate_facade() _NOEXCEPT + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + for (unsigned int i = 0; i < rank; ++i) + elems[i] = {}; + } + _CONSTEXPR coordinate_facade(value_type e0) _NOEXCEPT + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + static_assert(rank == 1, "This constructor can only be used with rank == 1."); + elems[0] = e0; + } + // Preconditions: il.size() == rank + _CONSTEXPR coordinate_facade(std::initializer_list il) + { + static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); + fail_fast_assert(il.size() == rank); + for (unsigned int i = 0; i < rank; ++i) + { + elems[i] = begin(il)[i]; + } + } + + _CONSTEXPR coordinate_facade(const coordinate_facade & other) = default; + + template + _CONSTEXPR coordinate_facade(const coordinate_facade & other) + { + for (unsigned int i = 0; i < rank; ++i) + { + fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); + elems[i] = static_cast(other.elems[i]); + } + } + protected: + coordinate_facade& operator=(const coordinate_facade& rhs) = default; + // Preconditions: component_idx < rank + _CONSTEXPR reference operator[](unsigned int component_idx) + { + fail_fast_assert(component_idx < rank); + return elems[component_idx]; + } + // Preconditions: component_idx < rank + _CONSTEXPR const_reference operator[](unsigned int component_idx) const + { + fail_fast_assert(component_idx < rank); + return elems[component_idx]; + } + _CONSTEXPR bool operator==(const ConcreteType& rhs) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (elems[i] != rhs.elems[i]) + return false; + } + return true; + } + _CONSTEXPR bool operator!=(const ConcreteType& rhs) const _NOEXCEPT + { + return !(to_concrete() == rhs); + } + _CONSTEXPR ConcreteType operator+() const _NOEXCEPT + { + return to_concrete(); + } + _CONSTEXPR ConcreteType operator-() const + { + ConcreteType ret = to_concrete(); + for (unsigned int i = 0; i < rank; ++i) + ret.elems[i] = -ret.elems[i]; + return ret; + } + _CONSTEXPR ConcreteType operator+(const ConcreteType& rhs) const + { + ConcreteType ret = to_concrete(); + ret += rhs; + return ret; + } + _CONSTEXPR ConcreteType operator-(const ConcreteType& rhs) const + { + ConcreteType ret = to_concrete(); + ret -= rhs; + return ret; + } + _CONSTEXPR ConcreteType& operator+=(const ConcreteType& rhs) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] += rhs.elems[i]; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator-=(const ConcreteType& rhs) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] -= rhs.elems[i]; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator++() + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ++elems[0]; + return to_concrete(); + } + _CONSTEXPR ConcreteType operator++(int) + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ConcreteType ret = to_concrete(); + ++(*this); + return ret; + } + _CONSTEXPR ConcreteType& operator--() + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + --elems[0]; + return to_concrete(); + } + _CONSTEXPR ConcreteType operator--(int) + { + static_assert(rank == 1, "This operator can only be used with rank == 1."); + ConcreteType ret = to_concrete(); + --(*this); + return ret; + } + _CONSTEXPR ConcreteType operator*(value_type v) const + { + ConcreteType ret = to_concrete(); + ret *= v; + return ret; + } + _CONSTEXPR ConcreteType operator/(value_type v) const + { + ConcreteType ret = to_concrete(); + ret /= v; + return ret; + } + friend _CONSTEXPR ConcreteType operator*(value_type v, const ConcreteType& rhs) + { + return rhs * v; + } + _CONSTEXPR ConcreteType& operator*=(value_type v) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] *= v; + return to_concrete(); + } + _CONSTEXPR ConcreteType& operator/=(value_type v) + { + for (unsigned int i = 0; i < rank; ++i) + elems[i] /= v; + return to_concrete(); + } + value_type elems[rank]; + private: + _CONSTEXPR const ConcreteType& to_concrete() const _NOEXCEPT + { + return static_cast(*this); + } + _CONSTEXPR ConcreteType& to_concrete() _NOEXCEPT + { + return static_cast(*this); + } + }; + template + class arrow_proxy + { + public: + explicit arrow_proxy(T t) + : val(t) + {} + const T operator*() const _NOEXCEPT + { + return val; + } + const T* operator->() const _NOEXCEPT + { + return &val; + } + private: + T val; + }; +} + +template +class index : private details::coordinate_facade, ValueType, Rank> +{ + using Base = details::coordinate_facade, ValueType, Rank>; + friend Base; + +public: + using Base::rank; + using reference = typename Base::reference; + using const_reference = typename Base::const_reference; + using size_type = typename Base::value_type; + using value_type = typename Base::value_type; + _CONSTEXPR index() _NOEXCEPT : Base(){} + template > + _CONSTEXPR index(value_type e0) _NOEXCEPT : Base(e0){} + _CONSTEXPR index(std::initializer_list il) : Base(il){} + + _CONSTEXPR index(const index &) = default; + + template + _CONSTEXPR index(const index &other) : Base(other) + { + } + + using Base::operator[]; + using Base::operator==; + using Base::operator!=; + using Base::operator+; + using Base::operator-; + using Base::operator+=; + using Base::operator-=; + using Base::operator++; + using Base::operator--; + using Base::operator*; + using Base::operator/; + using Base::operator*=; + using Base::operator/=; +}; + +template +class index<1, ValueType> +{ + template + friend class index; +public: + static const unsigned int rank = 1; + using reference = ValueType&; + using const_reference = const ValueType&; + using size_type = ValueType; + using value_type = ValueType; + + _CONSTEXPR index() _NOEXCEPT : value(0) + { + } + _CONSTEXPR index(value_type e0) _NOEXCEPT : value(e0) + { + } + // Preconditions: il.size() == rank + _CONSTEXPR index(std::initializer_list il) + { + fail_fast_assert(il.size() == rank); + value = begin(il)[0]; + } + + _CONSTEXPR index(const index &) = default; + + template + _CONSTEXPR index(const index<1, OtherValueType> & other) + { + fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); + value = static_cast(other.value); + } + + + // Preconditions: component_idx < rank + _CONSTEXPR reference operator[](size_type component_idx) _NOEXCEPT + { + fail_fast_assert(component_idx == 0); + (void)(component_idx); + return value; + } + // Preconditions: component_idx < rank + _CONSTEXPR const_reference operator[](size_type component_idx) const _NOEXCEPT + { + fail_fast_assert(component_idx == 0); + (void)(component_idx); + return value; + } + _CONSTEXPR bool operator==(const index& rhs) const _NOEXCEPT + { + return value == rhs.value; + } + _CONSTEXPR bool operator!=(const index& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + _CONSTEXPR index operator+() const _NOEXCEPT + { + return *this; + } + _CONSTEXPR index operator-() const _NOEXCEPT + { + return index(-value); + } + _CONSTEXPR index operator+(const index& rhs) const _NOEXCEPT + { + return index(value + rhs.value); + } + _CONSTEXPR index operator-(const index& rhs) const _NOEXCEPT + { + return index(value - rhs.value); + } + _CONSTEXPR index& operator+=(const index& rhs) _NOEXCEPT + { + value += rhs.value; + return *this; + } + _CONSTEXPR index& operator-=(const index& rhs) _NOEXCEPT + { + value -= rhs.value; + return *this; + } + _CONSTEXPR index& operator++() _NOEXCEPT + { + ++value; + return *this; + } + _CONSTEXPR index operator++(int) _NOEXCEPT + { + index ret = *this; + ++(*this); + return ret; + } + _CONSTEXPR index& operator--() _NOEXCEPT + { + --value; + return *this; + } + _CONSTEXPR index operator--(int) _NOEXCEPT + { + index ret = *this; + --(*this); + return ret; + } + _CONSTEXPR index operator*(value_type v) const _NOEXCEPT + { + return index(value * v); + } + _CONSTEXPR index operator/(value_type v) const _NOEXCEPT + { + return index(value / v); + } + _CONSTEXPR index& operator*=(value_type v) _NOEXCEPT + { + value *= v; + return *this; + } + _CONSTEXPR index& operator/=(value_type v) _NOEXCEPT + { + value /= v; + return *this; + } + friend _CONSTEXPR index operator*(value_type v, const index& rhs) _NOEXCEPT + { + return index(rhs * v); + } +private: + value_type value; +}; + +#ifndef _MSC_VER + +struct static_bounds_dynamic_range_t +{ + template ::value>> + constexpr operator T() const noexcept + { + return static_cast(-1); + } + + template ::value>> + constexpr bool operator ==(T other) const noexcept + { + return static_cast(-1) == other; + } + + template ::value>> + constexpr bool operator !=(T other) const noexcept + { + return static_cast(-1) != other; + } + +}; + +template ::value>> +constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right == left; +} + +template ::value>> +constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept +{ + return right != left; +} + +constexpr static_bounds_dynamic_range_t dynamic_range{}; +#else +const char dynamic_range = -1; +#endif + +struct generalized_mapping_tag {}; +struct contiguous_mapping_tag : generalized_mapping_tag {}; + +namespace details +{ + template + struct StaticSizeHelperImpl + { + static_assert(static_cast(Fact1) * static_cast(Fact2) <= SizeTypeTraits::max_value, "Value out of the range of SizeType"); + static const SizeType value = Fact1 * Fact2; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = ConstBound; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = ConstBound; + }; + + template + struct StaticSizeHelperImpl + { + static const SizeType value = static_cast(ConstBound); + }; + + template + struct StaticSizeHelper + { + static const SizeType value = StaticSizeHelperImpl(Fact1), static_cast(Fact2), static_cast(dynamic_range)>::value; + }; + + + template + struct LessThan + { + static const bool value = Left < Right; + }; + + template + struct BoundsRanges { + static const unsigned int Depth = 0; + static const unsigned int DynamicNum = 0; + static const SizeType CurrentRange = 1; + static const SizeType TotalSize = 1; + + BoundsRanges (const BoundsRanges &) = default; + + // TODO : following signature is for work around VS bug + template + BoundsRanges (const OtherType &, bool firstLevel) {} + BoundsRanges(const SizeType * const arr) { } + BoundsRanges() = default; + + + template + void serialize(T &) const { + } + template + SizeType linearize(const T &) const { + return 0; + } + template + ptrdiff_t contains(const T &) const { + return 0; + } + + size_t totalSize() const _NOEXCEPT { + return TotalSize; + } + + bool operator == (const BoundsRanges &) const _NOEXCEPT + { + return true; + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + static const unsigned int Depth = Base::Depth + 1; + static const unsigned int DynamicNum = Base::DynamicNum + 1; + static const SizeType CurrentRange = dynamic_range; + static const SizeType TotalSize = dynamic_range; + const SizeType m_bound; + + BoundsRanges (const BoundsRanges &) = default; + BoundsRanges(const SizeType * const arr) : Base(arr + 1), m_bound(static_cast(*arr * this->Base::totalSize())) + { + fail_fast_assert(0 <= *arr); + fail_fast_assert(*arr * this->Base::totalSize() <= details::SizeTypeTraits::max_value); + } + BoundsRanges() : m_bound(0) {} + + template + BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : + Base(static_cast&>(other), false), m_bound (static_cast(other.totalSize())) + { + } + + template + void serialize(T & arr) const { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + template + SizeType linearize(const T & arr) const { + const size_t index = this->Base::totalSize() * arr[Dim]; + fail_fast_assert(index < static_cast(m_bound)); + return static_cast(index) + this->Base::template linearize(arr); + } + + template + ptrdiff_t contains(const T & arr) const { + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; + return static_cast(cur) < static_cast(m_bound) ? cur + last : -1; + } + + size_t totalSize() const _NOEXCEPT { + return m_bound; + } + + SizeType elementNum() const _NOEXCEPT { + return static_cast(totalSize() / this->Base::totalSize()); + } + + SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + { + return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRanges : BoundsRanges{ + using Base = BoundsRanges ; + static const unsigned int Depth = Base::Depth + 1; + static const unsigned int DynamicNum = Base::DynamicNum; + static const SizeType CurrentRange = static_cast(CurRange); + static const SizeType TotalSize = StaticSizeHelper::value; + static_assert (CurRange <= SizeTypeTraits::max_value, "CurRange must be smaller than SizeType limits"); + + BoundsRanges (const BoundsRanges &) = default; + BoundsRanges(const SizeType * const arr) : Base(arr) { } + BoundsRanges() = default; + + template + BoundsRanges(const BoundsRanges &other, bool firstLevel = true) : Base(static_cast&>(other), false) + { + fail_fast_assert((firstLevel && totalSize() <= other.totalSize()) || totalSize() == other.totalSize()); + } + + template + void serialize(T & arr) const { + arr[Dim] = elementNum(); + this->Base::template serialize(arr); + } + + template + SizeType linearize(const T & arr) const { + fail_fast_assert(arr[Dim] < CurrentRange); + return static_cast(this->Base::totalSize()) * arr[Dim] + this->Base::template linearize(arr); + } + + template + ptrdiff_t contains(const T & arr) const { + if (static_cast(arr[Dim]) >= CurrentRange) + return -1; + const ptrdiff_t last = this->Base::template contains(arr); + if (last == -1) + return -1; + return static_cast(this->Base::totalSize() * arr[Dim]) + last; + } + + size_t totalSize() const _NOEXCEPT{ + return CurrentRange * this->Base::totalSize(); + } + + SizeType elementNum() const _NOEXCEPT{ + return CurrentRange; + } + + SizeType elementNum(unsigned int dim) const _NOEXCEPT{ + if (dim > 0) + return this->Base::elementNum(dim - 1); + else + return elementNum(); + } + + bool operator == (const BoundsRanges & rhs) const _NOEXCEPT + { + return static_cast(*this) == static_cast(rhs); + } + }; + + template + struct BoundsRangeConvertible2; + + // TODO: I have to rewrite BoundsRangeConvertible into following way to workaround VS 2013 bugs + template > + auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; + + template + auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; + + template + struct BoundsRangeConvertible2 : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant())) + {}; + + template + struct BoundsRangeConvertible2 : std::true_type {}; + + template + struct BoundsRangeConvertible : decltype(helpBoundsRangeConvertible(SourceType(), TargetType(), + std::integral_constant::value || TargetType::CurrentRange == dynamic_range || SourceType::CurrentRange == dynamic_range)>())) + {}; + template + struct BoundsRangeConvertible : std::true_type {}; + + template + struct TypeListIndexer + { + const TypeChain & obj; + TypeListIndexer(const TypeChain & obj) :obj(obj){} + template + const TypeChain & getObj(std::true_type) + { + return obj; + } + template + auto getObj(std::false_type) -> decltype(TypeListIndexer(static_cast(obj)).template get()) + { + return TypeListIndexer(static_cast(obj)).template get(); + } + template + auto get() -> decltype(getObj(std::integral_constant())) + { + return getObj(std::integral_constant()); + } + }; + + template + TypeListIndexer createTypeListIndexer(const TypeChain &obj) + { + return TypeListIndexer(obj); + } +} + +template +class bounds_iterator; + +template +class static_bounds { +public: + static_bounds(const details::BoundsRanges &empty) { + } +}; + +template +class static_bounds +{ + using MyRanges = details::BoundsRanges ; + static_assert(std::is_integral::value + && details::SizeTypeTraits::max_value <= SIZE_MAX, "SizeType must be an integral type and its numeric limits must be smaller than SIZE_MAX"); + + MyRanges m_ranges; + _CONSTEXPR static_bounds(const MyRanges & range) : m_ranges(range) { } + + template + friend class static_bounds; +public: + static const unsigned int rank = MyRanges::Depth; + static const unsigned int dynamic_rank = MyRanges::DynamicNum; + static const SizeType static_size = static_cast(MyRanges::TotalSize); + + using size_type = SizeType; + using index_type = index; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + using difference_type = ptrdiff_t; + using sliced_type = static_bounds; + using mapping_type = contiguous_mapping_tag; +public: + _CONSTEXPR static_bounds(const static_bounds &) = default; + + template , details::BoundsRanges >::value>> + _CONSTEXPR static_bounds(const static_bounds &other): + m_ranges(other.m_ranges) + { + } + + _CONSTEXPR static_bounds(std::initializer_list il) : m_ranges(il.begin()) + { + fail_fast_assert(MyRanges::DynamicNum == il.size() && m_ranges.totalSize() <= details::SizeTypeTraits::max_value); + } + + _CONSTEXPR static_bounds() = default; + + _CONSTEXPR static_bounds & operator = (const static_bounds & otherBounds) + { + new(&m_ranges) MyRanges (otherBounds.m_ranges); + return *this; + } + + _CONSTEXPR sliced_type slice() const _NOEXCEPT + { + return sliced_type{static_cast &>(m_ranges)}; + } + + _CONSTEXPR size_type stride() const _NOEXCEPT + { + return rank > 1 ? slice().size() : 1; + } + + _CONSTEXPR size_type size() const _NOEXCEPT + { + return static_cast(m_ranges.totalSize()); + } + + _CONSTEXPR size_type linearize(const index_type & idx) const + { + return m_ranges.linearize(idx); + } + + _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + { + return m_ranges.contains(idx) != -1; + } + + _CONSTEXPR size_type operator[](unsigned int index) const _NOEXCEPT + { + return m_ranges.elementNum(index); + } + + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return details::createTypeListIndexer(m_ranges).template get().elementNum(); + } + + _CONSTEXPR index_type index_bounds() const _NOEXCEPT + { + index_type extents; + m_ranges.serialize(extents); + return extents; + } + + template + _CONSTEXPR bool operator == (const static_bounds & rhs) const _NOEXCEPT + { + return this->size() == rhs.size(); + } + + template + _CONSTEXPR bool operator != (const static_bounds & rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + + _CONSTEXPR const_iterator begin() const _NOEXCEPT + { + return const_iterator(*this); + } + + _CONSTEXPR const_iterator end() const _NOEXCEPT + { + index_type boundary; + m_ranges.serialize(boundary); + return const_iterator(*this, this->index_bounds()); + } +}; + +template +class strided_bounds : private details::coordinate_facade, SizeType, Rank> +{ + using Base = details::coordinate_facade, SizeType, Rank>; + friend Base; + _CONSTEXPR void makeRegularStriae() _NOEXCEPT + { + strides[rank - 1] = 1; + for (int i = rank - 2; i >= 0; i--) + strides[i] = strides[i + 1] * Base::elems[i + 1]; + } +public: + using Base::rank; + using reference = typename Base::reference; + using const_reference = typename Base::const_reference; + using size_type = typename Base::value_type; + using difference_type = typename Base::value_type; + using value_type = typename Base::value_type; + using index_type = index; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; + static const int dynamic_rank = rank; + static const size_t static_size = dynamic_range; + using sliced_type = std::conditional_t, void>; + using mapping_type = generalized_mapping_tag; + _CONSTEXPR strided_bounds() _NOEXCEPT : Base(), strides() {} + _CONSTEXPR strided_bounds(const strided_bounds &) = default; + + template + _CONSTEXPR strided_bounds(const strided_bounds &other) : Base(other) + { + } + + _CONSTEXPR strided_bounds(const index_type &extents, const index_type &stride) + : strides(stride) + { + for (unsigned int i = 0; i < rank; i++) + Base::elems[i] = extents[i]; + } + _CONSTEXPR strided_bounds(std::initializer_list il) + : Base(il) + { +#ifndef NDEBUG + for (const auto& v : il) + { + fail_fast_assert(v >= 0); + } +#endif + makeRegularStriae(); + } + index_type strides; + _CONSTEXPR size_type size() const _NOEXCEPT + { + size_type ret = 0; + for (unsigned int i = 0; i < rank; ++i) + ret += (Base::elems[i] - 1) * strides[i]; + return ret; + } + _CONSTEXPR bool contains(const index_type& idx) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (idx[i] < 0 || idx[i] >= Base::elems[i]) + return false; + } + return true; + } + _CONSTEXPR size_type linearize(const index_type & idx) const + { + size_type ret = 0; + for (unsigned int i = 0; i < rank; i++) + { + fail_fast_assert(idx[i] < Base::elems[i]); + ret += idx[i] * strides[i]; + } + return ret; + } + _CONSTEXPR sliced_type slice() const + { + sliced_type ret; + for (unsigned int i = 1; i < rank; ++i) + { + ret.elems[i - 1] = Base::elems[i]; + ret.strides[i - 1] = strides[i]; + } + return ret; + } + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return Base::elems[Dim]; + } + _CONSTEXPR index_type index_bounds() const _NOEXCEPT + { + index_type extents; + for (unsigned int i = 0; i < rank; ++i) + extents[i] = (*this)[i]; + return extents; + } + const_iterator begin() const _NOEXCEPT + { + return const_iterator{ *this }; + } + const_iterator end() const _NOEXCEPT + { + return const_iterator{ *this, index_bounds() }; + } +}; + +template +struct is_bounds : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; +template +struct is_bounds> : std::integral_constant {}; + +template +class bounds_iterator + : public std::iterator, + const IndexType> +{ +private: + using Base = std::iterator , const IndexType>; +public: + static const unsigned int rank = IndexType::rank; + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename IndexType::size_type; + template + explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) _NOEXCEPT + : boundary(bnd.index_bounds()) + , curr( std::move(curr) ) + { + static_assert(is_bounds::value, "Bounds type must be provided"); + } + reference operator*() const _NOEXCEPT + { + return curr; + } + pointer operator->() const _NOEXCEPT + { + return details::arrow_proxy{ curr }; + } + bounds_iterator& operator++() _NOEXCEPT + { + for (unsigned int i = rank; i-- > 0;) + { + if (++curr[i] < boundary[i]) + { + return *this; + } + else + { + curr[i] = 0; + } + } + // If we're here we've wrapped over - set to past-the-end. + for (unsigned int i = 0; i < rank; ++i) + { + curr[i] = boundary[i]; + } + return *this; + } + bounds_iterator operator++(int) _NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + bounds_iterator& operator--() _NOEXCEPT + { + for (int i = rank; i-- > 0;) + { + if (curr[i]-- > 0) + { + return *this; + } + else + { + curr[i] = boundary[i] - 1; + } + } + // If we're here the preconditions were violated + // "pre: there exists s such that r == ++s" + fail_fast_assert(false); + return *this; + } + bounds_iterator operator--(int) _NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + bounds_iterator operator+(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret += n; + } + bounds_iterator& operator+=(difference_type n) _NOEXCEPT + { + auto linear_idx = linearize(curr) + n; + value_type stride; + stride[rank - 1] = 1; + for (unsigned int i = rank - 1; i-- > 0;) + { + stride[i] = stride[i + 1] * boundary[i + 1]; + } + for (unsigned int i = 0; i < rank; ++i) + { + curr[i] = linear_idx / stride[i]; + linear_idx = linear_idx % stride[i]; + } + return *this; + } + bounds_iterator operator-(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret -= n; + } + bounds_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + { + return linearize(curr) - linearize(rhs.curr); + } + reference operator[](difference_type n) const _NOEXCEPT + { + return *(*this + n); + } + bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr == rhs.curr; + } + bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + { + for (unsigned int i = 0; i < rank; ++i) + { + if (curr[i] < rhs.curr[i]) + return true; + } + return false; + } + bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(bounds_iterator& rhs) _NOEXCEPT + { + std::swap(boundary, rhs.boundary); + std::swap(curr, rhs.curr); + } +private: + index_size_type linearize(const value_type& idx) const _NOEXCEPT + { + // TODO: Smarter impl. + // Check if past-the-end + bool pte = true; + for (unsigned int i = 0; i < rank; ++i) + { + if (idx[i] != boundary[i]) + { + pte = false; + break; + } + } + index_size_type multiplier = 1; + index_size_type res = 0; + if (pte) + { + res = 1; + for (unsigned int i = rank; i-- > 0;) + { + res += (idx[i] - 1) * multiplier; + multiplier *= boundary[i]; + } + } + else + { + for (unsigned int i = rank; i-- > 0;) + { + res += idx[i] * multiplier; + multiplier *= boundary[i]; + } + } + return res; + } + value_type boundary; + value_type curr; +}; + +template +class bounds_iterator> + : public std::iterator, + ptrdiff_t, + const details::arrow_proxy>, + const index<1, SizeType>> +{ + using Base = std::iterator, ptrdiff_t, const details::arrow_proxy>, const index<1, SizeType>>; + +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; + using index_type = value_type; + using index_size_type = typename index_type::size_type; + + template + explicit bounds_iterator(const Bounds &, value_type curr = value_type{}) _NOEXCEPT + : curr( std::move(curr) ) + {} + reference operator*() const _NOEXCEPT + { + return curr; + } + pointer operator->() const _NOEXCEPT + { + return details::arrow_proxy{ curr }; + } + bounds_iterator& operator++() _NOEXCEPT + { + ++curr; + return *this; + } + bounds_iterator operator++(int) _NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + bounds_iterator& operator--() _NOEXCEPT + { + curr--; + return *this; + } + bounds_iterator operator--(int) _NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + bounds_iterator operator+(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret += n; + } + bounds_iterator& operator+=(difference_type n) _NOEXCEPT + { + curr += n; + return *this; + } + bounds_iterator operator-(difference_type n) const _NOEXCEPT + { + bounds_iterator ret{ *this }; + return ret -= n; + } + bounds_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr[0] - rhs.curr[0]; + } + reference operator[](difference_type n) const _NOEXCEPT + { + return curr + n; + } + bool operator==(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr == rhs.curr; + } + bool operator!=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const bounds_iterator& rhs) const _NOEXCEPT + { + return curr[0] < rhs.curr[0]; + } + bool operator<=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const bounds_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const bounds_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(bounds_iterator& rhs) _NOEXCEPT + { + std::swap(curr, rhs.curr); + } +private: + value_type curr; +}; + +template +bounds_iterator operator+(typename bounds_iterator::difference_type n, const bounds_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +/* +** begin definitions of basic_array_view +*/ +namespace details +{ + template + _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + { + return bnd.strides; + } + + // Make a stride vector from bounds, assuming continugous memory. + template + _CONSTEXPR std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) _NOEXCEPT + { + auto extents = bnd.index_bounds(); + typename Bounds::index_type stride; + stride[Bounds::rank - 1] = 1; + for (int i = Bounds::rank - 2; i >= 0; --i) + stride[i] = stride[i + 1] * extents[i + 1]; + return stride; + } + + template + void verifyBoundsReshape(const BoundsSrc &src, const BoundsDest &dest) + { + static_assert(is_bounds::value && is_bounds::value, "The src type and dest type must be bounds"); + static_assert(std::is_same::value, "The source type must be a contiguous bounds"); + static_assert(BoundsDest::static_size == dynamic_range || BoundsSrc::static_size == dynamic_range || BoundsDest::static_size == BoundsSrc::static_size, "The source bounds must have same size as dest bounds"); + fail_fast_assert(src.size() == dest.size()); + } + + +} // namespace details + +template +class contiguous_array_view_iterator; +template +class general_array_view_iterator; +enum class byte : std::uint8_t {}; + +template +class basic_array_view +{ +public: + static const unsigned int rank = BoundsType::rank; + using bounds_type = BoundsType; + using size_type = typename bounds_type::size_type; + using index_type = typename bounds_type::index_type; + using value_type = ValueType; + using pointer = ValueType*; + using reference = ValueType&; + using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using sliced_type = std::conditional_t>; + +private: + pointer m_pdata; + bounds_type m_bounds; + +public: + _CONSTEXPR bounds_type bounds() const _NOEXCEPT + { + return m_bounds; + } + template + _CONSTEXPR size_type extent() const _NOEXCEPT + { + return m_bounds.template extent(); + } + _CONSTEXPR size_type size() const _NOEXCEPT + { + return m_bounds.size(); + } + _CONSTEXPR reference operator[](const index_type& idx) const + { + return m_pdata[m_bounds.linearize(idx)]; + } + _CONSTEXPR pointer data() const _NOEXCEPT + { + return m_pdata; + } + template > + _CONSTEXPR Ret operator[](size_type idx) const + { + const size_type ridx = idx * m_bounds.stride(); + fail_fast_assert(ridx < m_bounds.size()); + return Ret {m_pdata + ridx, m_bounds.slice()}; + } + + _CONSTEXPR operator bool () const _NOEXCEPT + { + return m_pdata != nullptr; + } + + _CONSTEXPR iterator begin() const + { + return iterator {this, true}; + } + _CONSTEXPR iterator end() const + { + return iterator {this}; + } + _CONSTEXPR const_iterator cbegin() const + { + return const_iterator {reinterpret_cast *>(this), true}; + } + _CONSTEXPR const_iterator cend() const + { + return const_iterator {reinterpret_cast *>(this)}; + } + + _CONSTEXPR reverse_iterator rbegin() const + { + return reverse_iterator {end()}; + } + _CONSTEXPR reverse_iterator rend() const + { + return reverse_iterator {begin()}; + } + _CONSTEXPR const_reverse_iterator crbegin() const + { + return const_reverse_iterator {cend()}; + } + _CONSTEXPR const_reverse_iterator crend() const + { + return const_reverse_iterator {cbegin()}; + } + + template , std::remove_cv_t>::value>> + _CONSTEXPR auto operator == (const basic_array_view & other) const -> decltype(this->m_pdata == other.m_pdata && this->m_bounds == other.m_bounds) _NOEXCEPT + { + return m_pdata == other.m_pdata && m_bounds == other.m_bounds; + } + +public: + template ::value + && std::is_convertible::value>> + _CONSTEXPR basic_array_view(const basic_array_view & other ) _NOEXCEPT + : m_pdata(other.m_pdata), m_bounds(other.m_bounds) + { + } +protected: + + _CONSTEXPR basic_array_view(pointer data, bounds_type bound) _NOEXCEPT + : m_pdata(data) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + _CONSTEXPR basic_array_view(T *data, std::enable_if_t>::value, bounds_type> bound) _NOEXCEPT + : m_pdata(reinterpret_cast(data)) + , m_bounds(std::move(bound)) + { + fail_fast_assert((m_bounds.size() > 0 && data != nullptr) || m_bounds.size() == 0); + } + template + _CONSTEXPR basic_array_view as_array_view(const DestBounds &bounds) + { + details::verifyBoundsReshape(m_bounds, bounds); + return {m_pdata, bounds}; + } +private: + + friend iterator; + friend const_iterator; + template + friend class basic_array_view; +}; + +template +struct dim +{ + static const size_t value = DimSize; +}; +template <> +struct dim +{ + static const size_t value = dynamic_range; + const size_t dvalue; + dim(size_t size) : dvalue(size) {} +}; + +template +class array_view; +template +class strided_array_view; + +namespace details +{ + template + struct ArrayViewTypeTraits + { + using value_type = T; + using size_type = size_t; + }; + + template + struct ArrayViewTypeTraits::type> + { + using value_type = typename Traits::array_view_traits::value_type; + using size_type = typename Traits::array_view_traits::size_type; + }; + + template + struct ArrayViewArrayTraits { + using type = array_view; + using value_type = T; + using bounds_type = static_bounds; + using pointer = T*; + using reference = T&; + }; + template + struct ArrayViewArrayTraits : ArrayViewArrayTraits {}; + + template + BoundsType newBoundsHelperImpl(size_t totalSize, std::true_type) // dynamic size + { + fail_fast_assert(totalSize <= details::SizeTypeTraits::max_value); + return BoundsType{static_cast(totalSize)}; + } + template + BoundsType newBoundsHelperImpl(size_t totalSize, std::false_type) // static size + { + fail_fast_assert(BoundsType::static_size == totalSize); + return {}; + } + template + BoundsType newBoundsHelper(size_t totalSize) + { + static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); + return newBoundsHelperImpl(totalSize, std::integral_constant()); + } + + struct Sep{}; + + template + T static_as_array_view_helper(Sep, Args... args) + { + return T{static_cast(args)...}; + } + template + std::enable_if_t>::value && !std::is_same::value, T> static_as_array_view_helper(Arg, Args... args) + { + return static_as_array_view_helper(args...); + } + template + T static_as_array_view_helper(dim val, Args ... args) + { + return static_as_array_view_helper(args..., val.dvalue); + } + + template + struct static_as_array_view_static_bounds_helper + { + using type = static_bounds; + }; + + template + struct is_array_view_oracle : std::false_type + {}; + template + struct is_array_view_oracle> : std::true_type + {}; + template + struct is_array_view_oracle> : std::true_type + {}; + template + struct is_array_view : is_array_view_oracle> + {}; + +} + + +template +struct array_view_options +{ + struct array_view_traits + { + using value_type = ValueType; + using size_type = SizeType; + }; +}; + +template +class array_view : public basic_array_view::value_type, + static_bounds::size_type, FirstDimension, RestDimensions...>> +{ + template + friend class array_view; + using Base = basic_array_view::value_type, + static_bounds::size_type, FirstDimension, RestDimensions...>>; + +public: + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + using Base::rank; + +public: + // basic + _CONSTEXPR array_view(pointer ptr, bounds_type bounds) : Base(ptr, std::move(bounds)) + { + } + + _CONSTEXPR array_view(std::nullptr_t) : Base(nullptr, bounds_type{}) + { + } + + _CONSTEXPR array_view(nullptr_t, size_type size) : Base(nullptr, bounds_type{}) + { + fail_fast_assert(size == 0); + } + + // default + template > + _CONSTEXPR array_view() : Base(nullptr, bounds_type()) + { + } + + // from n-dimensions dynamic array (e.g. new int[m][4]) (precedence will be lower than the 1-dimension pointer) + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value>> + _CONSTEXPR array_view(T * const & data, size_type size) : Base(data, typename Helper::bounds_type{size}) + { + } + + // from n-dimensions static array + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value>> + _CONSTEXPR array_view (T (&arr)[N]) : Base(arr, typename Helper::bounds_type()) + { + } + + // from n-dimensions static array with size + template , + typename Dummy = std::enable_if_t::value + && std::is_convertible::value >> + _CONSTEXPR array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) + { + fail_fast_assert(size <= N); + } + + // from std array + template , typename Base::bounds_type>::value>> + _CONSTEXPR array_view (std::array, N> & arr) : Base(arr.data(), static_bounds()) + { + } + + template , typename Base::bounds_type>::value && std::is_const::value>> + _CONSTEXPR array_view (const std::array, N> & arr) : Base(arr.data(), static_bounds()) + { + } + + + // from begin, end pointers. We don't provide iterator pair since no way to guarantee the contiguity + template ::value + && details::LessThan::value>> // remove literal 0 case + _CONSTEXPR array_view (pointer begin, Ptr end) : Base(begin, details::newBoundsHelper(static_cast(end) - begin)) + { + } + + // from containers. It must has .size() and .data() two function signatures + template ::value + && std::is_convertible::value + && std::is_convertible, typename Base::bounds_type>::value + && std::is_same().size(), *std::declval().data())>, DataType>::value> + > + _CONSTEXPR array_view (Cont& cont) : Base(static_cast(cont.data()), details::newBoundsHelper(cont.size())) + { + + } + + _CONSTEXPR array_view(const array_view &) = default; + + // convertible + template ::value_type, static_bounds::size_type, FirstDimension, RestDimensions...>>, + typename OtherBaseType = basic_array_view::value_type, static_bounds::size_type, OtherDimensions...>>, + typename Dummy = std::enable_if_t::value> + > + _CONSTEXPR array_view(const array_view &av) : Base(static_cast::Base &>(av)) {} // static_cast is required + + // reshape + template + _CONSTEXPR array_view as_array_view(Dimensions2... dims) + { + static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension."); + using BoundsType = typename array_view::bounds_type; + auto tobounds = details::static_as_array_view_helper(dims..., details::Sep{}); + details::verifyBoundsReshape(this->bounds(), tobounds); + return {this->data(), tobounds}; + } + + // to bytes array + template ::value_type>>::value> + _CONSTEXPR auto as_bytes() const _NOEXCEPT -> + array_view, static_cast(details::StaticSizeHelper::value)> + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + template ::value_type>>::value> + _CONSTEXPR auto as_writeable_bytes() const _NOEXCEPT -> + array_view, static_cast(details::StaticSizeHelper::value)> + { + static_assert(Enabled, "The value_type of array_view must be standarded layout"); + return { reinterpret_cast(this->data()), this->bytes() }; + } + + + // from bytes array + template::value, typename Dummy = std::enable_if_t> + _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + } + + template::value, typename Dummy = std::enable_if_t> + _CONSTEXPR auto as_array_view() const _NOEXCEPT -> array_view(dynamic_range))> + { + static_assert(std::is_standard_layout::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % sizeof(U) == 0), + "Target type must be standard layout and its size must match the byte array size"); + fail_fast_assert((this->bytes() % sizeof(U)) == 0); + return { reinterpret_cast(this->data()), this->bytes() / sizeof(U) }; + } + + // section on linear space + template + _CONSTEXPR array_view first() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); // ensures we only check condition when needed + return { this->data(), Count }; + } + + _CONSTEXPR array_view first(size_type count) const _NOEXCEPT + { + fail_fast_assert(count <= this->size()); + return { this->data(), count }; + } + + template + _CONSTEXPR array_view last() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || Count <= bounds_type::static_size, "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || Count <= this->size()); + return { this->data() + this->size() - Count, Count }; + } + + _CONSTEXPR array_view last(size_type count) const _NOEXCEPT + { + fail_fast_assert(count <= this->size()); + return { this->data() + this->size() - count, count }; + } + + template + _CONSTEXPR array_view sub() const _NOEXCEPT + { + static_assert(bounds_type::static_size == dynamic_range || ((Offset == 0 || Offset < bounds_type::static_size) && Offset + Count <= bounds_type::static_size), "Index is out of bound"); + fail_fast_assert(bounds_type::static_size != dynamic_range || ((Offset == 0 || Offset < this->size()) && Offset + Count <= this->size())); + return { this->data() + Offset, Count }; + } + + _CONSTEXPR array_view sub(size_type offset, size_type count) const _NOEXCEPT + { + fail_fast_assert((offset == 0 || offset < this->size()) && offset + count <= this->size()); + return { this->data() + offset, count }; + } + + // size + _CONSTEXPR size_type length() const _NOEXCEPT + { + return this->size(); + } + _CONSTEXPR size_type used_length() const _NOEXCEPT + { + return length(); + } + _CONSTEXPR size_type bytes() const _NOEXCEPT + { + return sizeof(value_type) * this->size(); + } + _CONSTEXPR size_type used_bytes() const _NOEXCEPT + { + return bytes(); + } + + // section + _CONSTEXPR strided_array_view section(index_type origin, index_type extents) const + { + return { &this->operator[](origin), strided_bounds {extents, details::make_stride(Base::bounds())}}; + } +}; + +template +_CONSTEXPR auto as_array_view(T * const & ptr, dim... args) -> array_view, Dimensions...> +{ + return {reinterpret_cast*>(ptr), details::static_as_array_view_helper>(args..., details::Sep{})}; +} + +template +_CONSTEXPR auto as_array_view (T * arr, size_t len) -> typename details::ArrayViewArrayTraits::type +{ + return {arr, len}; +} + +template +_CONSTEXPR auto as_array_view (T (&arr)[N]) -> typename details::ArrayViewArrayTraits::type +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(const std::array &arr) +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(const std::array &&) = delete; + +template +_CONSTEXPR array_view as_array_view(std::array &arr) +{ + return {arr}; +} + +template +_CONSTEXPR array_view as_array_view(T *begin, T *end) +{ + return {begin, end}; +} + +template +_CONSTEXPR auto as_array_view(Cont &arr) -> std::enable_if_t>::value, + array_view, dynamic_range>> +{ + return {arr.data(), arr.size()}; +} + +template +_CONSTEXPR auto as_array_view(Cont &&arr) -> std::enable_if_t>::value, + array_view, dynamic_range>> = delete; + +template +class strided_array_view : public basic_array_view::value_type, strided_bounds::size_type>> +{ + using Base = basic_array_view::value_type, strided_bounds::size_type>>; +public: + using Base::rank; + using typename Base::bounds_type; + using typename Base::size_type; + using typename Base::pointer; + using typename Base::value_type; + using typename Base::index_type; + + strided_array_view (pointer ptr, bounds_type bounds): Base(ptr, std::move(bounds)) + { + } + template > + strided_array_view (array_view av, index_type strides): Base(av.data(), bounds_type{av.bounds().index_bounds(), strides}) + { + } + // section + strided_array_view section(index_type origin, index_type extents) const + { + return { &this->operator[](origin), bounds_type {extents, details::make_stride(Base::bounds())}}; + } +}; + +template +class contiguous_array_view_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; +private: + template + friend class basic_array_view; + pointer m_pdata; + const ArrayView * m_validator; + void validateThis() const + { + fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size()); + } + contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } +public: + reference operator*() const _NOEXCEPT + { + validateThis(); + return *m_pdata; + } + pointer operator->() const _NOEXCEPT + { + validateThis(); + return m_pdata; + } + contiguous_array_view_iterator& operator++() _NOEXCEPT + { + ++m_pdata; + return *this; + } + contiguous_array_view_iterator operator++(int)_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + contiguous_array_view_iterator& operator--() _NOEXCEPT + { + --m_pdata; + return *this; + } + contiguous_array_view_iterator operator--(int)_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + contiguous_array_view_iterator operator+(difference_type n) const _NOEXCEPT + { + contiguous_array_view_iterator ret{ *this }; + return ret += n; + } + contiguous_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + { + m_pdata += n; + return *this; + } + contiguous_array_view_iterator operator-(difference_type n) const _NOEXCEPT + { + contiguous_array_view_iterator ret{ *this }; + return ret -= n; + } + contiguous_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata - rhs.m_pdata; + } + reference operator[](difference_type n) const _NOEXCEPT + { + return *(*this + n); + } + bool operator==(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata == rhs.m_pdata; + } + bool operator!=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_validator == rhs.m_validator); + return m_pdata < rhs.m_pdata; + } + bool operator<=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const contiguous_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(contiguous_array_view_iterator& rhs) _NOEXCEPT + { + std::swap(m_pdata, rhs.m_pdata); + std::swap(m_validator, rhs.m_validator); + } +}; + +template +contiguous_array_view_iterator operator+(typename contiguous_array_view_iterator::difference_type n, const contiguous_array_view_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +template +class general_array_view_iterator : public std::iterator +{ + using Base = std::iterator; +public: + using typename Base::reference; + using typename Base::pointer; + using typename Base::difference_type; + using typename Base::value_type; +private: + template + friend class basic_array_view; + const ArrayView * m_container; + typename ArrayView::iterator m_itr; + general_array_view_iterator(const ArrayView *container, bool isbegin = false) : + m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) + { + } +public: + reference operator*() const _NOEXCEPT + { + return (*m_container)[*m_itr]; + } + pointer operator->() const _NOEXCEPT + { + return &(*m_container)[*m_itr]; + } + general_array_view_iterator& operator++() _NOEXCEPT + { + ++m_itr; + return *this; + } + general_array_view_iterator operator++(int)_NOEXCEPT + { + auto ret = *this; + ++(*this); + return ret; + } + general_array_view_iterator& operator--() _NOEXCEPT + { + --m_itr; + return *this; + } + general_array_view_iterator operator--(int)_NOEXCEPT + { + auto ret = *this; + --(*this); + return ret; + } + general_array_view_iterator operator+(difference_type n) const _NOEXCEPT + { + general_array_view_iterator ret{ *this }; + return ret += n; + } + general_array_view_iterator& operator+=(difference_type n) _NOEXCEPT + { + m_itr += n; + return *this; + } + general_array_view_iterator operator-(difference_type n) const _NOEXCEPT + { + general_array_view_iterator ret{ *this }; + return ret -= n; + } + general_array_view_iterator& operator-=(difference_type n) _NOEXCEPT + { + return *this += -n; + } + difference_type operator-(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr - rhs.m_itr; + } + value_type operator[](difference_type n) const _NOEXCEPT + { + return (*m_container)[m_itr[n]];; + } + bool operator==(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr == rhs.m_itr; + } + bool operator !=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(*this == rhs); + } + bool operator<(const general_array_view_iterator& rhs) const _NOEXCEPT + { + fail_fast_assert(m_container == rhs.m_container); + return m_itr < rhs.m_itr; + } + bool operator<=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs < *this); + } + bool operator>(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return rhs < *this; + } + bool operator>=(const general_array_view_iterator& rhs) const _NOEXCEPT + { + return !(rhs > *this); + } + void swap(general_array_view_iterator& rhs) _NOEXCEPT + { + std::swap(m_itr, rhs.m_itr); + std::swap(m_container, rhs.m_container); + } +}; + +template +general_array_view_iterator operator+(typename general_array_view_iterator::difference_type n, const general_array_view_iterator& rhs) _NOEXCEPT +{ + return rhs + n; +} + +} // namespace Guide + +#pragma pop_macro("_NOEXCEPT") diff --git a/include/fail_fast.h b/include/fail_fast.h new file mode 100644 index 0000000..bd93cb7 --- /dev/null +++ b/include/fail_fast.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +namespace Guide +{ + +// +// Having "fail fast" result in an exception makes unit testing +// the GSL classes that rely upon it much simpler. +// +#if defined(SAFER_CPP_TESTING) + +struct fail_fast : public std::exception {}; +inline void fail_fast_assert(bool cond) { if (!cond) throw fail_fast(); } + +#else + +inline void fail_fast_assert(bool cond) { if (!cond) std::terminate(); } + +#endif // SAFER_CPP_TESTING + +} \ No newline at end of file diff --git a/include/gsl.h b/include/gsl.h new file mode 100644 index 0000000..f4c8857 --- /dev/null +++ b/include/gsl.h @@ -0,0 +1,284 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "array_view.h" // array_view, strided_array_view... +#include "string_view.h" // zstring, string_view, zstring_builder... +#include + +namespace Guide +{ + +// +// GSL.owner: ownership pointers +// +using std::unique_ptr; +using std::shared_ptr; + +// +// GSL.assert: assertions +// +#define Expects(x) Guide::fail_fast_assert((x)) +#define Ensures(x) Guide::fail_fast_assert((x)) + +// +// GSL.util: utilities +// + +// Final_act allows you to ensure something gets run at the end of a scope +template +class Final_act +{ +public: + explicit Final_act(F f) : f_(f) {} + + Final_act(const Final_act&& other) : f_(other.f_) {} + Final_act(const Final_act&) = delete; + Final_act& operator=(const Final_act&) = delete; + + ~Final_act() { f_(); } + +private: + F f_; +}; + +// finally() - convenience function to generate a Final_act +template +Final_act finally(F f) { return Final_act(f); } + +// narrow_cast(): a searchable way to do narrowing casts of values +template +T narrow_cast(U u) { return static_cast(u); } + +struct narrowing_error : public std::exception {}; +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template +T narrow(U u) { T t = narrow_cast(u); if (static_cast(t) != u) throw narrowing_error(); return t; } + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector +// +template +T& at(T(&arr)[N], size_t index) { fail_fast_assert(index < N); return arr[index]; } + +template +T& at(std::array& arr, size_t index) { fail_fast_assert(index < N); return arr[index]; } + +template +typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index < cont.size()); return cont[index]; } + + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* or U& +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from U* fails with nullptr +// - allow implicit conversion to U* +// +template +class not_null +{ +public: + not_null(T t) : ptr_(t) { ensure_invariant(); } + + // deleting these two prevents compilation when initialized with a nullptr or literal 0 + not_null(std::nullptr_t) = delete; + not_null(int) = delete; + + not_null(const not_null &other) = default; + + template ::value>> + not_null(const not_null &other) : ptr_(other.get()) + { + } + + not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } + + // prevents compilation when someone attempts to assign a nullptr + not_null& operator=(std::nullptr_t) = delete; + not_null& operator=(int) = delete; + + T get() const { +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } // the assume() should help the optimizer + + operator T() const { return get(); } + T operator->() const { return get(); } + + bool operator==(const T& rhs) const { return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } +private: + T ptr_; + + // we assume that the compiler can hoist/prove away most of the checks inlined from this function + // if not, we could make them optional via conditional compilation + void ensure_invariant() const { fail_fast_assert(ptr_ != nullptr); } + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+(size_t) = delete; + not_null& operator+=(size_t) = delete; + not_null& operator-(size_t) = delete; + not_null& operator-=(size_t) = delete; +}; + + +// +// maybe_null +// +// Describes an optional pointer - provides symmetry with not_null +// +template +class maybe_null_dbg +{ +public: + maybe_null_dbg() : ptr_(nullptr), tested_(false) {} + + maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} + maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} + + template ::value>> + maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) + { + } + + template ::value>> + maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.get()), tested_(false) + { + } + + maybe_null_dbg& operator=(const T& p) + { + if (ptr_ != p) + { + ptr_ = p; + tested_ = false; + } + return *this; + } + + maybe_null_dbg& operator=(const maybe_null_dbg& rhs) + { + if (this != &rhs) + { + ptr_ = rhs.ptr_; + tested_ = false; + } + return *this; + } + + bool present() const { tested_ = true; return ptr_ != nullptr; } + + bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } + bool operator!=(const T& rhs) const { return !(*this == rhs); } + + T get() const { + fail_fast_assert(tested_); +#ifdef _MSC_VER + __assume(ptr_ != nullptr); +#endif + return ptr_; + } + + operator T() const { return get(); } + T operator->() const { return get(); } + +private: + const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type + + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + maybe_null_dbg& operator++() = delete; + maybe_null_dbg& operator--() = delete; + maybe_null_dbg operator++(int) = delete; + maybe_null_dbg operator--(int) = delete; + maybe_null_dbg& operator+(size_t) = delete; + maybe_null_dbg& operator+=(size_t) = delete; + maybe_null_dbg& operator-(size_t) = delete; + maybe_null_dbg& operator-=(size_t) = delete; + + T ptr_; + mutable bool tested_; +}; + +template +class maybe_null_ret +{ +public: + maybe_null_ret() : ptr_(nullptr) {} + maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} + maybe_null_ret(const T& p) : ptr_(p) {} + maybe_null_ret(const maybe_null_ret& rhs) = default; + + template ::value>> + maybe_null_ret(const not_null &other) : ptr_(other.get()) + { + } + + template ::value>> + maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) + { + } + + template ::value>> + maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) + { + } + + maybe_null_ret& operator=(const T& p) { if (ptr_ != p) { ptr_ = p; } return *this; } + maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; + + bool present() const { return ptr_ != nullptr; } + + T get() const { return ptr_; } + + operator T() const { return get(); } + T operator->() const { return get(); } + +private: + // unwanted operators...pointers only point to single objects! + // TODO ensure all arithmetic ops on this type are unavailable + maybe_null_ret& operator++() = delete; + maybe_null_ret& operator--() = delete; + maybe_null_ret operator++(int) = delete; + maybe_null_ret operator--(int) = delete; + maybe_null_ret& operator+(size_t) = delete; + maybe_null_ret& operator+=(size_t) = delete; + maybe_null_ret& operator-(size_t) = delete; + maybe_null_ret& operator-=(size_t) = delete; + + const size_t ptee_size_ = sizeof(*ptr_); // T must be a pointer type + T ptr_; +}; + +template using maybe_null = maybe_null_ret; + +} // namespace Guide diff --git a/include/string_view.h b/include/string_view.h new file mode 100644 index 0000000..7becc8e --- /dev/null +++ b/include/string_view.h @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "array_view.h" +#include + +namespace Guide +{ +// +// czstring and wzstring +// +// These are "tag" typedef's for C-style strings (i.e. null-terminated character arrays) +// that allow static analysis to help find bugs. +// +// There are no additional features/semantics that we can find a way to add inside the +// type system for these types that will not either incur significant runtime costs or +// (sometimes needlessly) break existing programs when introduced. +// +template +using czstring = const char*; + +template +using cwzstring = const wchar_t*; + +template +using zstring = char*; + +template +using wzstring = wchar_t*; + +// +// string_view and relatives +// +// Note that Extent is always single-dimension only +// Note that SizeType is defaulted to be smaller than size_t which is the array_view default +// +// TODO (neilmac) once array_view regains configurable size_type, update these typedef's +// +template +using basic_string_view = array_view; + +template +using string_view = basic_string_view; + +template +using cstring_view = basic_string_view; + +template +using wstring_view = basic_string_view; + +template +using cwstring_view = basic_string_view; + + +// +// ensure_sentinel() +// +// Provides a way to obtain an array_view from a contiguous sequence +// that ends with a (non-inclusive) sentinel value. +// +// Will fail-fast if sentinel cannot be found before max elements are examined. +// +template +array_view ensure_sentinel(const T* seq, SizeType max = std::numeric_limits::max()) +{ + auto cur = seq; + while ((cur - seq) < max && *cur != Sentinel) ++cur; + fail_fast_assert(*cur == Sentinel); + return{ seq, cur - seq }; +} + + +// +// ensure_z - creates a string_view for a czstring or cwzstring. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// +template +inline basic_string_view ensure_z(T* const & sz, size_t max = std::numeric_limits::max()) +{ + return ensure_sentinel<0>(sz, max); +} + +// TODO (neilmac) there is probably a better template-magic way to get the const and non-const overloads to share an implementation +inline basic_string_view ensure_z(char* const & sz, size_t max) +{ + auto len = strnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, len }; +} + +inline basic_string_view ensure_z(const char* const& sz, size_t max) +{ + auto len = strnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, len }; +} + +inline basic_string_view ensure_z(wchar_t* const & sz, size_t max) +{ + auto len = wcsnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, len }; +} + +inline basic_string_view ensure_z(const wchar_t* const & sz, size_t max) +{ + auto len = wcsnlen(sz, max); + fail_fast_assert(sz[len] == 0); return{ sz, len }; +} + +template +basic_string_view ensure_z(T(&sz)[N]) { return ensure_z(&sz[0], N); } + +template +basic_string_view::type, dynamic_range> ensure_z(Cont& cont) +{ + return ensure_z(cont.data(), cont.length()); +} + +// +// to_string() allow (explicit) conversions from string_view to string +// +template +std::basic_string::type> to_string(const basic_string_view& view) +{ + return{ view.data(), view.length() }; +} + + +template +class basic_zstring_builder +{ +public: + using string_view_type = basic_string_view; + using value_type = CharT; + using pointer = CharT*; + using size_type = typename string_view_type::size_type; + using iterator = typename string_view_type::iterator; + + basic_zstring_builder(CharT* data, size_type length) : sv_(data, length) {} + + template + basic_zstring_builder(CharT(&arr)[Size]) : sv_(arr) {} + + pointer data() const { return sv_.data(); } + string_view_type view() const { return sv_; } + + size_type length() const { return sv_.length(); } + + pointer assume0() const { return data(); } + string_view_type ensure_z() const { return Guide::ensure_z(sv_); } + + iterator begin() const { return sv_.begin(); } + iterator end() const { return sv_.end(); } + +private: + string_view_type sv_; +}; + +template +using zstring_builder = basic_zstring_builder; + +template +using wzstring_builder = basic_zstring_builder; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..8f4385a --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,143 @@ +cmake_minimum_required(VERSION 3.2.2) + +project(GSLTests) + +add_subdirectory(unittest-cpp) + +include_directories( + ../include + ./unittest-cpp +) + +add_definitions(-DSAFER_CPP_TESTING) + +if(MSVC14 OR MSVC12) + # has the support we need +else() + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++14" COMPILER_SUPPORTS_CXX14) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + if(COMPILER_SUPPORTS_CXX14) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + elseif(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") + endif() +endif() + +if (NOT EXISTS tests/unittest-cpp) + message(FATAL_ERROR "Could not find unittest-cpp enlistment. Please run 'git clone https://github.com/Microsoft/unittest-cpp.git unittest-cpp' in the tests directory") +endif() + +add_executable(array_view_tests + array_view_tests.cpp +) +target_link_libraries(array_view_tests + UnitTest++ +) +install(TARGETS array_view_tests + RUNTIME DESTINATION bin +) +add_test( + NAME array_view_tests + COMMAND array_view_tests +) + +add_executable(string_view_tests + string_view_tests.cpp +) +target_link_libraries(string_view_tests + UnitTest++ +) +install(TARGETS string_view_tests + RUNTIME DESTINATION bin +) +add_test( + NAME string_view_tests + COMMAND string_view_tests +) + +add_executable(at_tests + at_tests.cpp +) +target_link_libraries(at_tests + UnitTest++ +) +install(TARGETS at_tests + RUNTIME DESTINATION bin +) +add_test( + NAME at_tests + COMMAND at_tests +) + +add_executable(bounds_tests + bounds_tests.cpp +) +target_link_libraries(bounds_tests + UnitTest++ +) +install(TARGETS bounds_tests + RUNTIME DESTINATION bin +) +add_test( + NAME bounds_tests + COMMAND bounds_tests +) + +add_executable(maybenull_tests + maybenull_tests.cpp +) +target_link_libraries(maybenull_tests + UnitTest++ +) +install(TARGETS maybenull_tests + RUNTIME DESTINATION bin +) +add_test( + NAME maybenull_tests + COMMAND maybenull_tests +) + +add_executable(notnull_tests + notnull_tests.cpp +) +target_link_libraries(notnull_tests + UnitTest++ +) +install(TARGETS notnull_tests + RUNTIME DESTINATION bin +) +add_test( + NAME notnull_tests + COMMAND notnull_tests +) + +add_executable(assertion_tests + assertion_tests.cpp +) +target_link_libraries(assertion_tests + UnitTest++ +) +install(TARGETS assertion_tests + RUNTIME DESTINATION bin +) +add_test( + NAME assertion_tests + COMMAND assertion_tests +) + +add_executable(utils_tests + utils_tests.cpp +) +target_link_libraries(utils_tests + UnitTest++ +) +install(TARGETS utils_tests + RUNTIME DESTINATION bin +) +add_test( + NAME utils_tests + COMMAND utils_tests +) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp new file mode 100644 index 0000000..a97bbe3 --- /dev/null +++ b/tests/array_view_tests.cpp @@ -0,0 +1,648 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; +using namespace Guide; + +namespace +{ + void use(int&) {} + struct BaseClass {}; + struct DerivedClass : BaseClass {}; +} + +SUITE(array_view_tests) +{ + TEST(basics) + { + auto ptr = as_array_view(new int[10], 10); + fill(ptr.begin(), ptr.end(), 99); + for (int num : ptr) + { + CHECK(num == 99); + } + + delete[] ptr.data(); + + + static_bounds bounds{ 3 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av(nullptr, bounds); + av.extent(); + av.extent<2>(); + av[8][4][3]; +#endif + } + + TEST (array_view_convertible) + { +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av1(nullptr, b1); +#endif + + auto f = [&]() { array_view av1(nullptr); }; + CHECK_THROW(f(), fail_fast); + + array_view av1(nullptr); + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b12(b11); + b12 = b11; + b11 = b12; + + array_view av1 = nullptr; + array_view av2(av1); + array_view av2(av1); +#endif + + array_view avd; +#ifdef CONFIRM_COMPILATION_ERRORS + array_view avb = avd; +#endif + array_view avcd = avd; + } + + TEST(boundary_checks) + { + int arr[10][2]; + auto av = as_array_view(arr); + + fill(begin(av), end(av), 0); + + av[2][0] = 1; + av[1][1] = 3; + + // out of bounds + CHECK_THROW(av[1][3] = 3, fail_fast); + CHECK_THROW((av[{1, 3}] = 3), fail_fast); + + CHECK_THROW(av[10][2], fail_fast); + CHECK_THROW((av[{10,2}]), fail_fast); + } + + void overloaded_func(array_view exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void overloaded_func(array_view exp, char expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + void fixed_func(array_view exp, int expected_value) { + for (auto val : exp) + { + CHECK(val == expected_value); + } + } + + TEST(array_view_parameter_test) + { + auto data = new int[4][3][5]; + + auto av = as_array_view(data, 4); + + CHECK(av.size() == 60); + + fill(av.begin(), av.end(), 34); + + int count = 0; + for_each(av.rbegin(), av.rend(), [&](int val) { count += val; }); + CHECK(count == 34 * 60); + overloaded_func(av, 34); + + overloaded_func(av.as_array_view(dim<>(4), dim<>(3), dim<>(5)), 34); + + //fixed_func(av, 34); + delete[] data; + } + + + TEST(md_access) + { + unsigned int width = 5, height = 20; + + unsigned int imgSize = width * height; + auto image_ptr = new int[imgSize][3]; + + // size check will be done + auto image_view = as_array_view(image_ptr, imgSize).as_array_view(dim<>(height), dim<>(width), dim<3>()); + + iota(image_view.begin(), image_view.end(), 1); + + int expected = 0; + for (unsigned int i = 0; i < height; i++) + { + for (unsigned int j = 0; j < width; j++) + { + CHECK(expected + 1 == image_view[i][j][0]); + CHECK(expected + 2 == image_view[i][j][1]); + CHECK(expected + 3 == image_view[i][j][2]); + + auto val = image_view[{i, j, 0}]; + CHECK(expected + 1 == val); + val = image_view[{i, j, 1}]; + CHECK(expected + 2 == val); + val = image_view[{i, j, 2}]; + CHECK(expected + 3 == val); + + expected += 3; + } + } + } + + TEST(array_view_factory_test) + { + { + int * arr = new int[150]; + + auto av = as_array_view(arr, dim<10>(), dim<>(3), dim<5>()); + + fill(av.begin(), av.end(), 24); + overloaded_func(av, 24); + + delete[] arr; + + + array stdarr{ 0 }; + auto av2 = as_array_view(stdarr); + overloaded_func(av2.as_array_view(dim<>(1), dim<3>(), dim<5>()), 0); + + + string str = "ttttttttttttttt"; // size = 15 + auto t = str.data(); + auto av3 = as_array_view(str); + overloaded_func(av3.as_array_view(dim<>(1), dim<3>(), dim<5>()), 't'); + } + + { + int a[3][4][5]; + auto av = as_array_view(a); + const int (*b)[4][5]; + b = a; + auto bv = as_array_view(b, 3); + + CHECK(av == bv); + + const std::array arr = {0.0, 0.0, 0.0}; + auto cv = as_array_view(arr); + + vector vec(3); + auto dv = as_array_view(vec); + +#ifdef CONFIRM_COMPILATION_ERRORS + auto dv2 = as_array_view(std::move(vec)); +#endif + } + } + + TEST (array_view_reshape_test) + { + int a[3][4][5]; + auto av = as_array_view(a); + auto av2 = av.as_array_view(dim<60>()); + auto av3 = av2.as_array_view(dim<3>(), dim<4>(), dim<5>()); + auto av4 = av3.as_array_view(dim<4>(), dim<>(3), dim<5>()); + auto av5 = av4.as_array_view(dim<3>(), dim<4>(), dim<5>()); + auto av6 = av5.as_array_view(dim<12>(), dim<>(5)); + + fill(av6.begin(), av6.end(), 1); + + auto av7 = av6.as_bytes(); + + auto av8 = av7.as_array_view(); + + CHECK(av8.size() == av6.size()); + for (size_t i = 0; i < av8.size(); i++) + { + CHECK(av8[i] == 1); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + struct Foo {char c[11];}; + auto av9 = av7.as_array_view(); +#endif + } + + + TEST (array_view_section_test) + { + int a[30][4][5]; + + auto av = as_array_view(a); + auto sub = av.section({15, 0, 0}, Guide::index<3>{2, 2, 2}); + auto subsub = sub.section({1, 0, 0}, Guide::index<3>{1, 1, 1}); + } + + + TEST(constructors) + { + array_view av(nullptr); + CHECK(av.length() == 0); + + array_view av2; + CHECK(av2.length() == 0); + + array_view av3(nullptr, 0); + CHECK(av3.length() == 0); + + // Constructing from a nullptr + length is specifically disallowed + auto f = [&]() {array_view av4(nullptr, 2);}; + CHECK_THROW(f(), fail_fast); + + int arr1[2][3]; + array_view av5(arr1); + + array arr2; + array_view av6(arr2); + + vector vec1(19); + array_view av7(vec1); + CHECK(av7.length() == 19); + + + array_view av8; + CHECK(av8.length() == 0); + array_view av9(arr2); + CHECK(av9.length() == 15); + + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view av10; + DerivedClass *p = nullptr; + array_view av11(p, 0); +#endif + + + } + + TEST(copyandassignment) + { + array_view av1; + + int arr[] = {3, 4, 5}; + av1 = arr; + array_view, dynamic_range> av2; + av2 = av1; + } + + TEST(array_view_first) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.first<2>().bounds() == static_bounds())); + CHECK(av.first<2>().length() == 2); + CHECK(av.first(2).length() == 2); + } + + { + array_view av = arr; + CHECK((av.first<0>().bounds() == static_bounds())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.first<5>().bounds() == static_bounds())); + CHECK(av.first<5>().length() == 5); + CHECK(av.first(5).length() == 5); + } + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK(av.first<6>().bounds() == static_bounds()); + CHECK(av.first<6>().length() == 6); +#endif + CHECK_THROW(av.first(6).length(), fail_fast); + } + + { + array_view av; + CHECK((av.first<0>().bounds() == static_bounds())); + CHECK(av.first<0>().length() == 0); + CHECK(av.first(0).length() == 0); + } + } + + TEST(array_view_last) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.last<2>().bounds() == static_bounds())); + CHECK(av.last<2>().length() == 2); + CHECK(av.last(2).length() == 2); + } + + { + array_view av = arr; + CHECK((av.last<0>().bounds() == static_bounds())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.last<5>().bounds() == static_bounds())); + CHECK(av.last<5>().length() == 5); + CHECK(av.last(5).length() == 5); + } + + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.last<6>().bounds() == static_bounds())); + CHECK(av.last<6>().length() == 6); +#endif + CHECK_THROW(av.last(6).length(), fail_fast); + } + + { + array_view av; + CHECK((av.last<0>().bounds() == static_bounds())); + CHECK(av.last<0>().length() == 0); + CHECK(av.last(0).length() == 0); + } + } + + TEST(custmized_array_view_size) + { + double (*arr)[3][4] = new double[100][3][4]; + array_view, dynamic_range, 3, 4> av1(arr, (char)10); + + struct EffectiveStructure + { + double* v1; + char v2; + }; + CHECK(sizeof(av1) == sizeof(EffectiveStructure)); + + CHECK_THROW(av1[10][3][4], fail_fast); + + array_view av2 = av1.as_array_view(dim<>(5), dim<6>(), dim<4>()); + + } + + TEST(array_view_sub) + { + int arr[5] = { 1, 2, 3, 4, 5 }; + + { + array_view av = arr; + CHECK((av.sub<2,2>().bounds() == static_bounds())); + CHECK((av.sub<2,2>().length() == 2)); + CHECK(av.sub(2,2).length() == 2); + } + + + { + array_view av = arr; + CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + } + + { + array_view av = arr; + CHECK((av.sub<0,5>().bounds() == static_bounds())); + CHECK((av.sub<0,5>().length() == 5)); + CHECK(av.sub(0,5).length() == 5); + } + + { + array_view av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + CHECK((av.sub<5,0>().bounds() == static_bounds())); + CHECK((av.sub<5,0>().length() == 0)); +#endif + CHECK_THROW(av.sub(5,0).length(), fail_fast); + } + + { + array_view av; + CHECK((av.sub<0,0>().bounds() == static_bounds())); + CHECK((av.sub<0,0>().length() == 0)); + CHECK(av.sub(0,0).length() == 0); + } + } + + void AssertNullEmptyProperties(array_view& av) + { + CHECK(av.length() == 0); + CHECK(av.data() == nullptr); + CHECK(!av); + } + + template + void AssertContentsMatch(T a1, U a2) + { + CHECK(a1.length() == a2.length()); + for (size_t i = 0; i < a1.length(); ++i) + CHECK(a1[i] == a2[i]); + } + + TEST(TestNullConstruction) + { + array_view av; + AssertNullEmptyProperties(av); + + array_view av2(nullptr); + AssertNullEmptyProperties(av2); + } + + TEST(ArrayConstruction) + { + int a[] = { 1, 2, 3, 4 }; + + array_view av = { &a[1], 3 }; + CHECK(av.length() == 3); + + array_view av3 = { a, 2 }; + CHECK(av3.length() == 2); + + array_view av2 = a; + CHECK(av2.length() == 4); + } + + TEST(NonConstConstConversions) + { + int a[] = { 1, 2, 3, 4 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view cav = a; + array_view av = cav; +#else + array_view av = a; + array_view cav = av; +#endif + AssertContentsMatch(av, cav); + } + + TEST(FixedSizeConversions) + { + int arr[] = { 1, 2, 3, 4 }; + + // converting to an array_view from an equal size array is ok + array_view av4 = arr; + CHECK(av4.length() == 4); + + // converting to dynamic_range a_v is always ok + { + array_view av = av4; + } + { + array_view av = arr; + } + + // initialization or assignment to static array_view that REDUCES size is NOT ok +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av2 = arr; + } + { + array_view av2 = av4; + } +#endif + + { + array_view av = arr; + array_view av2 = av; + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av = arr; + array_view av2 = av.as_array_view(dim<2>(), dim<2>()); + } +#endif + + { + array_view av = arr; + auto f = [&]() {array_view av2 = av.as_array_view(dim<>(2), dim<>(2));}; + CHECK_THROW(f(), fail_fast); + } + + // but doing so explicitly is ok + + // you can convert statically + { + array_view av2 = {arr, 2}; + } + { + array_view av2 = av4.first<1>(); + } + + // ...or dynamically + { + // NB: implicit conversion to array_view from array_view + array_view av2 = av4.first(1); + } + + // initialization or assignment to static array_view that requires size INCREASE is not ok. + int arr2[2] = { 1, 2 }; + +#ifdef CONFIRM_COMPILATION_ERRORS + { + array_view av4 = arr2; + } + { + array_view av2 = arr2; + array_view av4 = av2; + } +#endif + { + auto f = [&]() {array_view av4 = {arr2, 2};}; + CHECK_THROW(f(), fail_fast); + } + + // this should fail - we are trying to assign a small dynamic a_v to a fixed_size larger one + array_view av = arr2; + auto f = [&](){ array_view av2 = av; }; + CHECK_THROW(f(), fail_fast); + } + + TEST(AsWriteableBytes) + { + int a[] = { 1, 2, 3, 4 }; + + { +#ifdef CONFIRM_COMPILATION_ERRORS + // you should not be able to get writeable bytes for const objects + array_view av = a; + auto wav = av.as_writeable_bytes(); +#endif + } + + { + array_view av; + auto wav = av.as_writeable_bytes(); + CHECK(wav.length() == av.length()); + CHECK(wav.length() == 0); + CHECK(wav.bytes() == 0); + } + + { + array_view av = a; + auto wav = av.as_writeable_bytes(); + CHECK(wav.data() == (byte*)&a[0]); + CHECK(wav.length() == sizeof(a)); + } + + } + + + TEST(ArrayViewComparison) + { + int arr[10][2]; + auto av1 = as_array_view(arr); + array_view av2 = av1; + + CHECK(av1 == av2); + + array_view, 20> av3 = av1.as_array_view(dim<>(20)); + CHECK(av3 == av2 && av3 == av1); + + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/assertion_tests.cpp b/tests/assertion_tests.cpp new file mode 100644 index 0000000..012a043 --- /dev/null +++ b/tests/assertion_tests.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +using namespace Guide; + +SUITE(assertion_tests) +{ + int f(int i) + { + Expects(i > 0 && i < 10); + return i; + } + + TEST(expects) + { + CHECK(f(2) == 2); + CHECK_THROW(f(10), fail_fast); + } + + int g(int i) + { + i++; + Ensures(i > 0 && i < 10); + return i; + } + + TEST(ensures) + { + CHECK(g(2) == 3); + CHECK_THROW(g(9), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp new file mode 100644 index 0000000..6a86307 --- /dev/null +++ b/tests/at_tests.cpp @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace std; +using namespace Guide; + +SUITE(at_tests) +{ + TEST(static_array) + { + int a[] = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(std_array) + { + std::array a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } + + TEST(StdVector) + { + std::vector a = { 1, 2, 3, 4 }; + + for (int i = 0; i < 4; ++i) + CHECK(at(a, i) == i+1); + + CHECK_THROW(at(a, 4), fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/bounds_tests.cpp b/tests/bounds_tests.cpp new file mode 100644 index 0000000..b14a113 --- /dev/null +++ b/tests/bounds_tests.cpp @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace std; +using namespace Guide;; + +namespace +{ + void use(unsigned int&) {} +} + +SUITE(bounds_test) +{ + TEST(basic_bounds) + { + for (auto point : static_bounds { 2 }) + { + for (unsigned int j = 0; j < decltype(point)::rank; j++) + { + use(j); + use(point[j]); + } + } + } + + TEST(bounds_basic) + { + static_bounds b; + auto a = b.slice(); + static_bounds x{ 4 }; + x.slice().slice(); + } + + TEST (arrayview_iterator) + { + static_bounds bounds{ 3 }; + + auto itr = bounds.begin(); + +#ifdef CONFIRM_COMPILATION_ERRORS + array_view< int, 4, dynamic_range, 2> av(nullptr, bounds); + + auto itr2 = av.cbegin(); + + for (auto & v : av) { + v = 4; + } + fill(av.begin(), av.end(), 0); +#endif + } + + TEST (bounds_convertible) + { + static_bounds b1; + static_bounds b2 = b1; + +#ifdef CONFIRM_COMPILATION_ERRORS + static_bounds b4 = b2; +#endif + + static_bounds b3 = b1; + static_bounds b4 = b3; + + static_bounds b11; + + static_bounds b5; + static_bounds b6; + + b5 = static_bounds(); + CHECK_THROW(b6 = b5, fail_fast); + b5 = static_bounds(); + b6 = b5; + + CHECK(b5 == b6); + CHECK(b5.size() == b6.size()); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp new file mode 100644 index 0000000..1fdfb78 --- /dev/null +++ b/tests/maybenull_tests.cpp @@ -0,0 +1,197 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +using namespace Guide; + +struct MyBase { bool foo() { return true; } }; +struct MyDerived : public MyBase {}; +struct Unrelated {}; + +SUITE(MaybeNullTests) +{ + TEST(TestMaybeNull1) + { + int n = 5; + maybe_null_dbg opt_n(&n); + int result = 0; + bool threw = false; + + CHECK_THROW(result = *opt_n, fail_fast); + } + + TEST(TestMaybeNull2) + { + int n = 5; + maybe_null opt_n(&n); + int result = 0; + if (opt_n.present()) + result = *opt_n; + } + + TEST(TestMaybeNull3) + { + int n = 5; + maybe_null opt_n(&n); + int result = 0; + if (opt_n != nullptr) + result = *opt_n; + } + + int test4_helper(maybe_null p) + { + if (p != nullptr) + return *p; + return -1; + } + + TEST(TestMaybeNull4) + { + int n = 5; + int result = 0; + result = test4_helper(&n); + } + + int test5_helper(maybe_null_dbg p) + { + return *p; + } + + TEST(TestMaybeNull5) + { + int n = 5; + int result = 0; + bool threw = false; + + CHECK_THROW(result = test5_helper(&n), fail_fast); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + int TestMaybeNull6() + { + int n; + maybe_null o(n); + } +#endif + + int g_int; + void test7_helper(maybe_null *> outptr) + { + g_int = 5; + + if (outptr.present()) + *outptr = &g_int; + } + + void test7b_helper(maybe_null_dbg *> outptr) + { + g_int = 5; + + if (outptr.present()) + *outptr = &g_int; + } + + TEST(TestMaybeNull7a) + { + maybe_null outval; + test7_helper(&outval); + CHECK(outval.present() && *outval == 5); + } + + TEST(TestMaybeNull7b) + { + maybe_null_dbg outval; + test7b_helper(&outval); + CHECK_THROW((void)*outval, fail_fast); + } + + int test8_helper1(maybe_null_dbg opt) + { + return *opt; + } + + int test8_helper2a(maybe_null_dbg opt) + { + if (!opt.present()) + return 0; + return test8_helper1(opt); + } + + TEST(TestMaybeNull8a) + { + int n = 5; + maybe_null_dbg opt(&n); + CHECK_THROW(test8_helper2a(opt), fail_fast); + } + +#ifdef CONVERT_TO_PTR_TO_CONST + int test9_helper(maybe_null copt) + { + if (copt.present()) + return *copt; + return 0; + } + + void TestMaybeNull9() + { + int n = 5; + maybe_null opt(&n); + CHECK_THROW(test9_helper(opt), fail_fast); + } +#endif + + TEST(TestMaybeNullCasting) + { + MyDerived derived; + maybe_null p = &derived; + CHECK(p.present()); + + maybe_null q = p; + CHECK(q == p); + +#ifdef CONFIRM_COMPILATION_ERRORS + maybe_null r = p; + maybe_null s = reinterpret_cast(p); +#endif + maybe_null_dbg t = reinterpret_cast(p.get()); + + CHECK_THROW((void)(void*)t.get(), fail_fast); + maybe_null_dbg u = reinterpret_cast(p.get()); + CHECK(u.present()); + CHECK((void*)p.get() == (void*)u.get()); + } + + TEST(TestMaybeNullArrow) + { + MyDerived derived; + maybe_null_dbg p = &derived; + + CHECK_THROW(p->foo(), fail_fast); + CHECK(p.present()); + CHECK(p->foo()); + + maybe_null q = p; + CHECK(q.present()); + CHECK(q->foo()); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp new file mode 100644 index 0000000..008cbb3 --- /dev/null +++ b/tests/notnull_tests.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +using namespace Guide; + +struct MyBase {}; +struct MyDerived : public MyBase {}; +struct Unrelated {}; + +// stand-in for a user-defined ref-counted class +template +struct RefCounted +{ + RefCounted(T* p) : p_(p) {} + operator T*() { return p_; } + T* p_; +}; + +SUITE(NotNullTests) +{ + + bool helper(not_null p) + { + return *p == 12; + } + + TEST(TestNotNullConstructors) + { +#ifdef CONFIRM_COMPILATION_ERRORS + not_null p = nullptr; // yay...does not compile! + not_null*> p = 0; // yay...does not compile! + not_null p; // yay...does not compile! + std::unique_ptr up = std::make_unique(120); + not_null p = up; +#endif + int i = 12; + auto rp = RefCounted(&i); + not_null p(rp); + CHECK(p.get() == &i); + } + + TEST(TestNotNullCasting) + { + MyDerived derived; + not_null p = &derived; + not_null q = p; + CHECK(q == p); + +#ifdef CONFIRM_COMPILATION_ERRORS + not_null r = p; + not_null s = reinterpret_cast(p); +#endif + not_null t = reinterpret_cast(p.get()); + CHECK((void*)p.get() == (void*)t.get()); + } + + TEST(TestNotNullAssignment) + { + int i = 12; + not_null p = &i; + CHECK(helper(p)); + + int* q = nullptr; + CHECK_THROW(p = q, fail_fast); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/string_view_tests.cpp b/tests/string_view_tests.cpp new file mode 100644 index 0000000..c382cf0 --- /dev/null +++ b/tests/string_view_tests.cpp @@ -0,0 +1,104 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +using namespace std; +using namespace Guide; + +SUITE(string_view_tests) +{ + + TEST(TestLiteralConstruction) + { + cwstring_view<> v = ensure_z(L"Hello"); + + CHECK(5 == v.length()); + +#ifdef CONFIRM_COMPILATION_ERRORS + wstring_view<> v2 = ensure0(L"Hello"); +#endif + } + + TEST(TestConstructFromStdString) + { + std::string s = "Hello there world"; + cstring_view<> v = s; + CHECK(v.length() == s.length()); + } + + TEST(TestConstructFromStdVector) + { + std::vector vec('h', 5); + string_view<> v = vec; + CHECK(v.length() == vec.size()); + } + + TEST(TestStackArrayConstruction) + { + wchar_t stack_string[] = L"Hello"; + + { + cwstring_view<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + CHECK(v.used_length() == v.length()); + } + + { + cwstring_view<> v = stack_string; + CHECK(v.length() == 6); + CHECK(v.used_length() == v.length()); + } + + { + wstring_view<> v = ensure_z(stack_string); + CHECK(v.length() == 5); + CHECK(v.used_length() == v.length()); + } + + { + wstring_view<> v = stack_string; + CHECK(v.length() == 6); + CHECK(v.used_length() == v.length()); + } + } + + TEST(TestConversionToConst) + { + char stack_string[] = "Hello"; + string_view<> v = ensure_z(stack_string); + cstring_view<> v2 = v; + CHECK(v.length() == v2.length()); + } + + TEST(TestConversionFromConst) + { + char stack_string[] = "Hello"; + cstring_view<> v = ensure_z(stack_string); +#ifdef CONFIRM_COMPILATION_ERRORS + string_view<> v2 = v; + string_view<> v3 = "Hello"; +#endif + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +} diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp new file mode 100644 index 0000000..0dc4809 --- /dev/null +++ b/tests/utils_tests.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +using namespace Guide; + +SUITE(utils_tests) +{ + void f(int& i) + { + i += 1; + } + + TEST(finally_lambda) + { + int i = 0; + { + auto _ = finally([&]() {f(i);}); + CHECK(i == 0); + } + CHECK(i == 1); + } + + TEST(finally_function_with_bind) + { + int i = 0; + { + auto _ = finally(std::bind(&f, std::ref(i))); + CHECK(i == 0); + } + CHECK(i == 1); + } + + int j = 0; + void g() { j += 1; }; + TEST(finally_function_ptr) + { + j = 0; + { + auto _ = finally(&g); + CHECK(j == 0); + } + CHECK(j == 1); + } + + TEST(narrow_cast) + { + int n = 120; + char c = narrow_cast(n); + CHECK(c == 120); + + n = 300; + unsigned char uc = narrow_cast(n); + CHECK(uc == 44); + } + + TEST(narrow) + { + int n = 120; + char c = narrow(n); + CHECK(c == 120); + + n = 300; + CHECK_THROW(narrow(n), narrowing_error); + } +} + +int main(int, const char *[]) +{ + return UnitTest::RunAllTests(); +}