diff --git a/.travis.yml b/.travis.yml index 524f1fb..3c64230 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ matrix: packages: - clang-3.6 - cmake + - g++-5 sources: &sources - ubuntu-toolchain-r-test - llvm-toolchain-precise-3.6 diff --git a/README.md b/README.md index 7d96dec..c687673 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) +# GSL: Guidelines Support Library [![Build Status](https://travis-ci.org/Microsoft/GSL.svg?branch=master)](https://travis-ci.org/Microsoft/GSL) [![Build status](https://ci.appveyor.com/api/projects/status/github/Microsoft/GSL?svg=true)](https://ci.appveyor.com/project/neilmacintosh/GSL) The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). diff --git a/include/array_view.h b/include/array_view.h index 046cbf8..0145799 100644 --- a/include/array_view.h +++ b/include/array_view.h @@ -24,11 +24,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "fail_fast.h" #ifdef _MSC_VER @@ -70,382 +72,166 @@ namespace details template struct SizeTypeTraits { - static const size_t max_value = std::is_signed::value ? static_cast::type>(-1) / 2 : static_cast(-1); + static const SizeType max_value = std::numeric_limits::max(); }; - template - class coordinate_facade - { - static_assert(std::is_integral::value - && sizeof(ValueType) <= sizeof(size_t), "ValueType must be an integral type!"); - static_assert(Rank > 0, "Rank must be greater than 0!"); + template + class are_integral : public std::integral_constant {}; - template - friend class coordinate_facade; - public: - using reference = ValueType&; - using const_reference = const ValueType&; - using value_type = ValueType; - static const size_t rank = Rank; - constexpr coordinate_facade() noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - } - constexpr coordinate_facade(const value_type(&values)[rank]) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - for (size_t i = 0; i < rank; ++i) - elems[i] = values[i]; - } - constexpr coordinate_facade(value_type e0) noexcept - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - static_assert(rank == 1, "This constructor can only be used with rank == 1."); - elems[0] = e0; - } - // Preconditions: il.size() == rank - constexpr coordinate_facade(std::initializer_list il) - { - static_assert(std::is_base_of::value, "ConcreteType must be derived from coordinate_facade."); - fail_fast_assert(il.size() == rank, "The size of the initializer list must match the rank of the array"); - for (size_t i = 0; i < rank; ++i) - { - elems[i] = begin(il)[i]; - } - } - - constexpr coordinate_facade(const coordinate_facade & other) = default; - - template - constexpr coordinate_facade(const coordinate_facade & other) - { - for (size_t i = 0; i < rank; ++i) - { - fail_fast_assert(static_cast(other.elems[i]) <= SizeTypeTraits::max_value); - elems[i] = static_cast(other.elems[i]); - } - } - protected: - coordinate_facade& operator=(const coordinate_facade& rhs) = default; - // Preconditions: component_idx < rank - constexpr reference operator[](size_t component_idx) - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - // Preconditions: component_idx < rank - constexpr const_reference operator[](size_t component_idx) const - { - fail_fast_assert(component_idx < rank, "Component index must be less than rank"); - return elems[component_idx]; - } - constexpr bool operator==(const ConcreteType& rhs) const noexcept - { - return std::equal(elems, elems + rank, rhs.elems); - } - constexpr bool operator!=(const ConcreteType& rhs) const noexcept - { - return !(to_concrete() == rhs); - } - constexpr ConcreteType operator+() const noexcept - { - return to_concrete(); - } - constexpr ConcreteType operator-() const - { - ConcreteType ret = to_concrete(); - std::transform(ret, ret + rank, ret, std::negate{}); - return ret; - } - constexpr ConcreteType operator+(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret += rhs; - return ret; - } - constexpr ConcreteType operator-(const ConcreteType& rhs) const - { - ConcreteType ret = to_concrete(); - ret -= rhs; - return ret; - } - constexpr ConcreteType& operator+=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] += rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator-=(const ConcreteType& rhs) - { - for (size_t i = 0; i < rank; ++i) - elems[i] -= rhs.elems[i]; - return to_concrete(); - } - constexpr ConcreteType& operator++() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ++elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator++(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - ++(*this); - return ret; - } - constexpr ConcreteType& operator--() - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - --elems[0]; - return to_concrete(); - } - constexpr ConcreteType operator--(int) - { - static_assert(rank == 1, "This operator can only be used with rank == 1."); - ConcreteType ret = to_concrete(); - --(*this); - return ret; - } - constexpr ConcreteType operator*(value_type v) const - { - ConcreteType ret = to_concrete(); - ret *= v; - return ret; - } - constexpr ConcreteType operator/(value_type v) const - { - ConcreteType ret = to_concrete(); - ret /= v; - return ret; - } - friend constexpr ConcreteType operator*(value_type v, const ConcreteType& rhs) - { - return rhs * v; - } - constexpr ConcreteType& operator*=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] *= v; - return to_concrete(); - } - constexpr ConcreteType& operator/=(value_type v) - { - for (size_t i = 0; i < rank; ++i) - elems[i] /= v; - return to_concrete(); - } - value_type elems[rank] = {}; - private: - constexpr const ConcreteType& to_concrete() const noexcept - { - return static_cast(*this); - } - constexpr ConcreteType& to_concrete() noexcept - { - return static_cast(*this); - } - }; - template - class arrow_proxy - { - 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 are_integral : public std::integral_constant::value && are_integral::value> {}; } template -class index : private details::coordinate_facade, ValueType, Rank> +class index final { - using Base = details::coordinate_facade, ValueType, Rank>; - friend Base; + static_assert(std::is_integral::value, "ValueType must be an integral type!"); + static_assert(Rank > 0, "Rank must be greater than 0!"); + template friend class index; + public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using value_type = typename Base::value_type; - constexpr index() noexcept : Base(){} - constexpr index(const value_type (&values)[rank]) noexcept : Base(values) {} - constexpr index(std::initializer_list il) : Base(il) {} + static const size_t rank = Rank; + using value_type = std::remove_reference_t; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_lvalue_reference_t>; - constexpr index(const index &) = default; + constexpr index() noexcept + {} - template - constexpr index(const index &other) : Base(other) + constexpr index(const value_type(&values)[Rank]) noexcept { - } - constexpr static index shift_left(const index& other) noexcept - { - value_type (&arr)[rank] = (value_type(&)[rank])(*(other.elems + 1)); - return index(arr); + std::copy(values, values + Rank, elems); } - 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::value, typename Dummy = std::enable_if_t> + constexpr index(Ts... ds) noexcept : elems{ static_cast(ds)... } + {} -template -class index<1, ValueType> -{ - template - friend class index; -public: - static const size_t rank = 1; - using reference = ValueType&; - using const_reference = const ValueType&; - using size_type = ValueType; - using value_type = ValueType; - - constexpr index() noexcept : value(0) + constexpr index(const index& other) noexcept = default; + + // copy from index over smaller domain + template ::max_value <= details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& other) noexcept { - } - constexpr index(value_type e0) noexcept : value(e0) - { - } - constexpr index(const value_type(&values)[1]) noexcept : index(values[0]) - { - } - // Preconditions: il.size() == rank - constexpr index(std::initializer_list il) - { - fail_fast_assert(il.size() == rank, "Size of the initializer list must match the rank of the array"); - value = begin(il)[0]; + std::copy(other.elems, other.elems + Rank, elems); } - constexpr index(const index &) = default; - - template - constexpr index(const index<1, OtherValueType> & other) + // copy from index over larger domain + template ::max_value > details::SizeTypeTraits::max_value), + typename Other = std::enable_if_t>> + constexpr index(const index& other, void* ptr = 0) noexcept { - fail_fast_assert(other.value <= details::SizeTypeTraits::max_value); - value = static_cast(other.value); + bool ok = std::accumulate(other.elems, other.elems + Rank, true, + [&](bool b, OtherValueType val) { return b && (val <= static_cast(details::SizeTypeTraits::max_value)); } + ); + + fail_fast_assert(ok, "other value must fit in the new domain"); + std::transform(other.elems, other.elems + rank, elems, [&](OtherValueType val) { return static_cast(val); }); } - constexpr static index shift_left(const index& other) noexcept - { - return other.elems[1]; - } + constexpr index& operator=(const index& rhs) noexcept = default; + // Preconditions: component_idx < rank - constexpr reference operator[](size_type component_idx) noexcept + constexpr reference operator[](size_t component_idx) { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); - return value; + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; } + // Preconditions: component_idx < rank - constexpr const_reference operator[](size_type component_idx) const noexcept + constexpr const_reference operator[](size_t component_idx) const noexcept { - fail_fast_assert(component_idx == 0, "Component index must be less than rank"); - (void)(component_idx); - return value; + fail_fast_assert(component_idx < Rank, "Component index must be less than rank"); + return elems[component_idx]; } + constexpr bool operator==(const index& rhs) const noexcept { - return value == rhs.value; + return std::equal(elems, elems + rank, rhs.elems); } + constexpr bool operator!=(const index& rhs) const noexcept { - return !(*this == rhs); + return !(this == rhs); } + constexpr index operator+() const noexcept { return *this; } + constexpr index operator-() const noexcept { - return index(-value); + index ret = *this; + std::transform(ret, ret + rank, ret, std::negate{}); + return ret; } + constexpr index operator+(const index& rhs) const noexcept { - return index(value + rhs.value); + index ret = *this; + ret += rhs; + return ret; } + constexpr index operator-(const index& rhs) const noexcept { - return index(value - rhs.value); + index ret = *this; + ret -= rhs; + return ret; } + constexpr index& operator+=(const index& rhs) noexcept { - value += rhs.value; + std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); return *this; } + constexpr index& operator-=(const index& rhs) noexcept { - value -= rhs.value; + std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); 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); + index ret = *this; + ret *= v; + return ret; } + 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; + index ret = *this; + ret /= v; + return ret; } + friend constexpr index operator*(value_type v, const index& rhs) noexcept { - return index(rhs * v); + return rhs * v; } + + constexpr index& operator*=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); + return *this; + } + + constexpr index& operator/=(value_type v) noexcept + { + std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); + return *this; + } + private: - value_type value; + value_type elems[Rank] = {}; }; #ifndef _MSC_VER @@ -751,6 +537,17 @@ namespace details { return TypeListIndexer(obj); } + + template 1), typename Ret = std::enable_if_t>> + constexpr Ret shift_left(const index& other) noexcept + { + Ret ret; + for (size_t i = 0; i < Rank - 1; ++i) + { + ret[i] = other[i + 1]; + } + return ret; + } } template @@ -782,8 +579,9 @@ public: using size_type = SizeType; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; using difference_type = ptrdiff_t; using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; @@ -855,9 +653,9 @@ public: constexpr index_type index_bounds() const noexcept { - index_type extents; + size_type extents[rank] = {}; m_ranges.serialize(extents); - return extents; + return{ extents }; } template @@ -874,90 +672,82 @@ public: constexpr const_iterator begin() const noexcept { - return const_iterator(*this); + return const_iterator(*this, index_type{}); } constexpr const_iterator end() const noexcept { - index_type boundary; - m_ranges.serialize(boundary); return const_iterator(*this, this->index_bounds()); } }; template -class strided_bounds : private details::coordinate_facade, SizeType, Rank> +class strided_bounds { - using Base = details::coordinate_facade, SizeType, Rank>; - friend Base; template friend class strided_bounds; public: - using Base::rank; - using reference = typename Base::reference; - using const_reference = typename Base::const_reference; - using size_type = typename Base::value_type; - using difference_type = typename Base::value_type; - using value_type = typename Base::value_type; + static const size_t rank = Rank; + using reference = SizeType&; + using const_reference = const SizeType&; + using size_type = SizeType; + using difference_type = SizeType; + using value_type = SizeType; using index_type = index; - using iterator = bounds_iterator; - using const_iterator = bounds_iterator; + using const_index_type = std::add_const_t; + using iterator = bounds_iterator; + using const_iterator = bounds_iterator; static const int dynamic_rank = rank; static const size_t static_size = dynamic_range; using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds &) = default; + constexpr strided_bounds(const strided_bounds &) noexcept = default; template - constexpr strided_bounds(const strided_bounds &other) - : Base(other), m_strides(other.strides) - { - } - - constexpr strided_bounds(const index_type &extents, const index_type &strides) - : m_strides(strides) - { - for (size_t i = 0; i < rank; i++) - Base::elems[i] = extents[i]; - } - constexpr strided_bounds(const value_type(&values)[rank], index_type strides) - : Base(values), m_strides(std::move(strides)) - { - } + constexpr strided_bounds(const strided_bounds &other) noexcept + : m_extents(other.extents), m_strides(other.strides) + {} + constexpr strided_bounds(const index_type &extents, const index_type &strides) noexcept + : m_extents(extents), m_strides(strides) + {} constexpr index_type strides() const noexcept - { - return m_strides; + { + return m_strides; } constexpr size_type total_size() const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; ++i) - ret += (Base::elems[i] - 1) * m_strides[i]; + { + ret += (m_extents[i] - 1) * m_strides[i]; + } return ret + 1; } constexpr size_type size() const noexcept { size_type ret = 1; for (size_t i = 0; i < rank; ++i) - ret *= Base::elems[i]; + { + ret *= m_extents[i]; + } return ret; } constexpr bool contains(const index_type& idx) const noexcept { for (size_t i = 0; i < rank; ++i) { - if (idx[i] < 0 || idx[i] >= Base::elems[i]) + if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; } return true; } - constexpr size_type linearize(const index_type & idx) const + constexpr size_type linearize(const index_type & idx) const noexcept { size_type ret = 0; for (size_t i = 0; i < rank; i++) { - fail_fast_assert(idx[i] < Base::elems[i], "index is out of bounds of the array"); + fail_fast_assert(idx[i] < m_extents[i], "index is out of bounds of the array"); ret += idx[i] * m_strides[i]; } return ret; @@ -969,27 +759,28 @@ public: template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const { - return{ (value_type(&)[rank - 1])Base::elems[1], sliced_type::index_type::shift_left(m_strides) }; + return{ details::shift_left(m_extents), details::shift_left(m_strides) }; } template constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); - return Base::elems[Dim]; + return m_extents[Dim]; } constexpr index_type index_bounds() const noexcept { - return index_type(Base::elems); + return m_extents; } - const_iterator begin() const noexcept + constexpr const_iterator begin() const noexcept { - return const_iterator{ *this }; + return const_iterator{ *this, index_type{} }; } - const_iterator end() const noexcept + constexpr const_iterator end() const noexcept { return const_iterator{ *this, index_bounds() }; } private: + index_type m_extents; index_type m_strides; }; @@ -1001,15 +792,11 @@ template struct is_bounds> : std::integral_constant {}; template -class bounds_iterator - : public std::iterator, - const IndexType> +class bounds_iterator: public std::iterator { private: - using Base = std::iterator , const IndexType>; + using Base = std::iterator ; + public: static const size_t rank = IndexType::rank; using typename Base::reference; @@ -1017,81 +804,90 @@ public: using typename Base::difference_type; using typename Base::value_type; using index_type = value_type; - using index_size_type = typename IndexType::size_type; + using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds & bnd, value_type curr = value_type{}) noexcept - : boundary(bnd.index_bounds()) - , curr( std::move(curr) ) + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary(bnd.index_bounds()), curr(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - reference operator*() const noexcept + + constexpr reference operator*() const noexcept { return curr; } - pointer operator->() const noexcept + + constexpr pointer operator->() const noexcept { - return details::arrow_proxy{ curr }; + return &curr; } - bounds_iterator& operator++() noexcept + + constexpr bounds_iterator& operator++() noexcept { for (size_t i = rank; i-- > 0;) { - if (++curr[i] < boundary[i]) + if (curr[i] < boundary[i] - 1) { + curr[i]++; return *this; } - else - { - curr[i] = 0; - } + curr[i] = 0; } // If we're here we've wrapped over - set to past-the-end. - for (size_t i = 0; i < rank; ++i) - { - curr[i] = boundary[i]; - } + curr = boundary; return *this; } - bounds_iterator operator++(int) noexcept + + constexpr bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - bounds_iterator& operator--() noexcept + + constexpr bounds_iterator& operator--() noexcept { - for (size_t i = rank; i-- > 0;) + if (!less(curr, boundary)) { - if (curr[i]-- > 0) - { - return *this; - } - else + // if at the past-the-end, set to last element + for (size_t i = 0; i < rank; ++i) { curr[i] = boundary[i] - 1; } + return *this; + } + for (size_t i = rank; i-- > 0;) + { + if (curr[i] >= 1) + { + curr[i]--; + return *this; + } + curr[i] = boundary[i] - 1; } // If we're here the preconditions were violated // "pre: there exists s such that r == ++s" fail_fast_assert(false); return *this; } - bounds_iterator operator--(int) noexcept + + constexpr bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - bounds_iterator operator+(difference_type n) const noexcept + + constexpr bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret += n; } - bounds_iterator& operator+=(difference_type n) noexcept + + constexpr bounds_iterator& operator+=(difference_type n) noexcept { auto linear_idx = linearize(curr) + n; - value_type stride; + std::remove_const_t stride; stride[rank - 1] = 1; for (size_t i = rank - 1; i-- > 0;) { @@ -1102,76 +898,84 @@ public: curr[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } + fail_fast_assert(!less(curr, index_type{}) && !less(boundary, curr), "index is out of bounds of the array"); return *this; } - bounds_iterator operator-(difference_type n) const noexcept + + constexpr bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{ *this }; return ret -= n; } - bounds_iterator& operator-=(difference_type n) noexcept + + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - difference_type operator-(const bounds_iterator& rhs) const noexcept + + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr) - linearize(rhs.curr); } - reference operator[](difference_type n) const noexcept + + constexpr reference operator[](difference_type n) const noexcept { return *(*this + n); } - bool operator==(const bounds_iterator& rhs) const noexcept + + constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr == rhs.curr; } - bool operator!=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } - bool operator<(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept { - for (size_t i = 0; i < rank; ++i) - { - if (curr[i] < rhs.curr[i]) - return true; - } - return false; + return less(curr, rhs.curr); } - bool operator<=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - bool operator>(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - bool operator>=(const bounds_iterator& rhs) const noexcept + + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } + void swap(bounds_iterator& rhs) noexcept { std::swap(boundary, rhs.boundary); std::swap(curr, rhs.curr); } private: - index_size_type linearize(const value_type& idx) const noexcept + constexpr bool less(index_type& one, index_type& other) const noexcept + { + for (size_t i = 0; i < rank; ++i) + { + if (one[i] < other[i]) + return true; + } + return false; + } + + constexpr index_size_type linearize(const value_type& idx) const noexcept { // TODO: Smarter impl. // Check if past-the-end - bool pte = true; - for (size_t 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) + if (!less(idx, boundary)) { res = 1; for (size_t i = rank; i-- > 0;) @@ -1190,119 +994,9 @@ private: } 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; + std::remove_const_t curr; }; template @@ -1327,11 +1021,14 @@ namespace details constexpr std::enable_if_t::value, typename Bounds::index_type> make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); - typename Bounds::index_type stride; - stride[Bounds::rank - 1] = 1; - for (size_t i = Bounds::rank - 1; Bounds::rank > 1 && i > 0; --i) - stride[i-1] = stride[i] * extents[i]; - return stride; + typename Bounds::size_type stride[Bounds::rank] = {}; + + stride[Bounds::rank - 1] = 1; + for (size_t i = 1; i < Bounds::rank; ++i) + { + stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; + } + return{ stride }; } template @@ -1361,10 +1058,11 @@ public: using size_type = typename bounds_type::size_type; using index_type = typename bounds_type::index_type; using value_type = ValueType; + using const_value_type = std::add_const_t; using pointer = ValueType*; using reference = ValueType&; using iterator = std::conditional_t::value, contiguous_array_view_iterator, general_array_view_iterator>; - using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; + using const_iterator = std::conditional_t::value, contiguous_array_view_iterator>, general_array_view_iterator>>; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; using sliced_type = std::conditional_t>; @@ -1417,7 +1115,7 @@ public: } constexpr iterator end() const { - return iterator {this}; + return iterator {this, false}; } constexpr const_iterator cbegin() const { @@ -1425,7 +1123,7 @@ public: } constexpr const_iterator cend() const { - return const_iterator {reinterpret_cast *>(this)}; + return const_iterator {reinterpret_cast *>(this), false}; } constexpr reverse_iterator rbegin() const @@ -2056,8 +1754,8 @@ private: { fail_fast_assert(m_pdata >= m_validator->m_pdata && m_pdata < m_validator->m_pdata + m_validator->size(), "iterator is out of range of the array"); } - contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : - m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { } + contiguous_array_view_iterator (const ArrayView *container, bool isbegin) : + m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {} public: reference operator*() const noexcept { @@ -2172,16 +1870,16 @@ private: friend class basic_array_view; const ArrayView * m_container; typename ArrayView::bounds_type::iterator m_itr; - general_array_view_iterator(const ArrayView *container, bool isbegin = false) : + general_array_view_iterator(const ArrayView *container, bool isbegin) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) { } public: - reference operator*() const noexcept + reference operator*() noexcept { return (*m_container)[*m_itr]; } - pointer operator->() const noexcept + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } diff --git a/include/gsl.h b/include/gsl.h index 824ca6a..ec75723 100644 --- a/include/gsl.h +++ b/include/gsl.h @@ -195,195 +195,21 @@ private: not_null& operator-=(size_t) = delete; }; - -// -// maybe_null -// -// Describes an optional pointer - provides symmetry with not_null -// -template -class maybe_null_ret; - -template -class maybe_null_dbg -{ - template - friend class maybe_null_dbg; - - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); -public: - maybe_null_dbg() : ptr_(nullptr), tested_(false) {} - maybe_null_dbg(std::nullptr_t) : ptr_(nullptr), tested_(false) {} - - maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} - maybe_null_dbg& operator=(const T& p) - { - if (ptr_ != p) - { - ptr_ = p; - tested_ = false; - } - return *this; - } - - - maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {} - maybe_null_dbg& operator=(const maybe_null_dbg& rhs) - { - if (this != &rhs) - { - ptr_ = rhs.ptr_; - tested_ = false; - } - return *this; - } - - - template ::value>> - maybe_null_dbg(const not_null &other) : ptr_(other.get()), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const not_null &other) - { - ptr_ = other.get(); - tested_ = false; - return *this; - } - - - template ::value>> - maybe_null_dbg(const maybe_null_dbg &other) : ptr_(other.ptr_), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const maybe_null_dbg &other) - { - ptr_ = other.ptr_; - tested_ = false; - return *this; - } - - - template ::value>> - maybe_null_dbg(const maybe_null_ret &other) : ptr_(other.get()), tested_(false) {} - - template ::value>> - maybe_null_dbg& operator=(const maybe_null_ret &other) - { - ptr_ = other.get(); - tested_ = false; - return *this; - } - - - bool present() const { tested_ = true; return ptr_ != nullptr; } - - bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } - bool operator!=(const T& rhs) const { return !(*this == rhs); } - template ::value>> - bool operator==(const maybe_null_dbg& rhs) const { tested_ = true; rhs.tested_ = true; return ptr_ == rhs.ptr_; } - template ::value>> - bool operator!=(const maybe_null_dbg& rhs) const { return !(*this == rhs); } - - T get() const { - fail_fast_assert(tested_); -#ifdef _MSC_VER - __assume(ptr_ != nullptr); -#endif - return ptr_; - } - - operator T() const { return get(); } - T operator->() const { return get(); } - -private: - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - maybe_null_dbg& operator++() = delete; - maybe_null_dbg& operator--() = delete; - maybe_null_dbg operator++(int) = delete; - maybe_null_dbg operator--(int) = delete; - maybe_null_dbg& operator+(size_t) = delete; - maybe_null_dbg& operator+=(size_t) = delete; - maybe_null_dbg& operator-(size_t) = delete; - maybe_null_dbg& operator-=(size_t) = delete; - - T ptr_; - mutable bool tested_; -}; - -template -class maybe_null_ret -{ - static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); -public: - maybe_null_ret() : ptr_(nullptr) {} - maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} - - maybe_null_ret(const T& p) : ptr_(p) {} - maybe_null_ret& operator=(const T& p) { ptr_ = p; return *this; } - - maybe_null_ret(const maybe_null_ret& rhs) = default; - maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; - - template ::value>> - maybe_null_ret(const not_null &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const not_null &other) - { - ptr_ = other.get(); - return *this; - } - - - template ::value>> - maybe_null_ret(const maybe_null_ret &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const maybe_null_ret &other) - { - ptr_ = other.get(); - return *this; - } - - - template ::value>> - maybe_null_ret(const maybe_null_dbg &other) : ptr_(other.get()) {} - - template ::value>> - maybe_null_ret& operator=(const maybe_null_dbg &other) - { - ptr_ = other.get(); - return *this; - } - - - bool present() const { return ptr_ != nullptr; } - - T get() const { return ptr_; } - - operator T() const { return get(); } - T operator->() const { return get(); } - -private: - // unwanted operators...pointers only point to single objects! - // TODO ensure all arithmetic ops on this type are unavailable - maybe_null_ret& operator++() = delete; - maybe_null_ret& operator--() = delete; - maybe_null_ret operator++(int) = delete; - maybe_null_ret operator--(int) = delete; - maybe_null_ret& operator+(size_t) = delete; - maybe_null_ret& operator+=(size_t) = delete; - maybe_null_ret& operator-(size_t) = delete; - maybe_null_ret& operator-=(size_t) = delete; - - T ptr_; -}; - -template using maybe_null = maybe_null_ret; - } // namespace gsl +namespace std +{ + template + struct hash> + { + size_t operator()(const gsl::not_null & value) const + { + return hash{}(value); + } + }; + +} // namespace std + #ifdef _MSC_VER #undef constexpr diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 774413f..5e4c395 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -47,7 +47,6 @@ add_gsl_test(array_view_tests) add_gsl_test(string_view_tests) add_gsl_test(at_tests) add_gsl_test(bounds_tests) -add_gsl_test(maybenull_tests) add_gsl_test(notnull_tests) add_gsl_test(assertion_tests) add_gsl_test(utils_tests) diff --git a/tests/array_view_tests.cpp b/tests/array_view_tests.cpp index 918df9e..3a8acc2 100644 --- a/tests/array_view_tests.cpp +++ b/tests/array_view_tests.cpp @@ -16,15 +16,11 @@ #include #include -#include -#include + #include #include #include #include -#include -#include - using namespace std; using namespace gsl; @@ -554,7 +550,7 @@ SUITE(array_view_tests) { // zero stride - strided_array_view sav{ av, {{4}, {}} }; + strided_array_view sav{ av,{ { 4 },{} } }; CHECK(sav[0] == 0); CHECK(sav[3] == 0); CHECK_THROW(sav[4], fail_fast); @@ -562,7 +558,7 @@ SUITE(array_view_tests) { // zero extent - strided_array_view sav{ av,{ {},{1} } }; + strided_array_view sav{ av,{ {},{ 1 } } }; CHECK_THROW(sav[0], fail_fast); } @@ -635,27 +631,17 @@ SUITE(array_view_tests) strided_array_view sav5{ av.as_array_view(dim<2>(), dim<2>()), { 1 } }; strided_array_view sav6{ av.as_array_view(dim<2>(), dim<2>()), { 1,1,1 } }; strided_array_view sav7{ av.as_array_view(dim<2>(), dim<2>()), { { 1,1 },{ 1,1 },{ 1,1 } } }; - } -#endif - - { - // stride initializer list size should match the rank of the array - CHECK_THROW((index<1>{ 0,1 }), fail_fast); - CHECK_THROW((strided_array_view{ arr, {1, {1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ arr, {{1,1 }, {1,1}} }), fail_fast); -#endif - CHECK_THROW((strided_array_view{ av, {1, {1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av, {{1,1 }, {1,1}} }), fail_fast); -#endif - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast); - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast); -#ifdef _MSC_VER - CHECK_THROW((strided_array_view{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast); -#endif - } + index<1> index{ 0, 1 }; + strided_array_view sav8{ arr,{ 1,{ 1,1 } } }; + strided_array_view sav9{ arr,{ { 1,1 },{ 1,1 } } }; + strided_array_view sav10{ av,{ 1,{ 1,1 } } }; + strided_array_view sav11{ av,{ { 1,1 },{ 1,1 } } }; + strided_array_view sav12{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1 } } }; + strided_array_view sav13{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } }; + strided_array_view sav14{ av.as_array_view(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } }; + } +#endif } TEST(strided_array_view_type_conversion) @@ -839,6 +825,94 @@ SUITE(array_view_tests) delete[] arr; } + TEST(index_constructors) + { + { + // components of the same type + index<3> i1(0, 1, 2); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + size_t c1 = 1; + index<3> i2(c0, c1, 2); + CHECK(i2[0] == 0); + + // from array + index<3> i3 = { 0,1,2 }; + CHECK(i3[0] == 0); + + // from other index of the same size type + index<3> i4 = i3; + CHECK(i4[0] == 0); + + // from other index of bigger size type + index<3, short> i5 = i4; + CHECK(i5[0] == 0); + + // from other index of smaller size type + index<3, long long> i6 = i4; + CHECK(i6[0] == 0); + + // default + index<3, long long> i7; + CHECK(i7[0] == 0); + + // default + index<3, long long> i9 = {}; + CHECK(i9[0] == 0); + } + + { + // components of the same type + index<1> i1(0); + CHECK(i1[0] == 0); + + // components of different types + size_t c0 = 0; + index<1> i2(c0); + CHECK(i2[0] == 0); + + // from array + index<1> i3 = { 0 }; + CHECK(i3[0] == 0); + + // from int + index<1> i4 = 0; + CHECK(i4[0] == 0); + + // from other index of the same size type + index<1> i5 = i3; + CHECK(i5[0] == 0); + + // from other index of bigger size type + index<1, short> i6 = i5; + CHECK(i6[0] == 0); + + // from other index of smaller size type + index<1, long long> i7 = i6; + CHECK(i7[0] == 0); + + // default + index<1, long long> i8; + CHECK(i8[0] == 0); + + // default + index<1, long long> i9 = {}; + CHECK(i9[0] == 0); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + index<3> i1(0, 1); + index<3> i2(0, 1, 2, 3); + index<3> i3 = { 0 }; + index<3> i4 = { 0, 1, 2, 3 }; + index<1> i5 = { 0,1 }; + } +#endif + } + TEST(index_operations) { size_t a[3] = { 0, 1, 2 }; @@ -873,7 +947,18 @@ SUITE(array_view_tests) } { - index<2> k = index<2>::shift_left(i); + index<3> k = 3 * i; + + CHECK(i[0] == 0); + CHECK(i[1] == 1); + CHECK(i[2] == 2); + CHECK(k[0] == 0); + CHECK(k[1] == 3); + CHECK(k[2] == 6); + } + + { + index<2> k = details::shift_left(i); CHECK(i[0] == 0); CHECK(i[1] == 1); @@ -914,11 +999,35 @@ SUITE(array_view_tests) } } - size_t idx = 0; - for (auto num : section) + size_t check_sum = 0; + for (size_t i = 0; i < length; ++i) { - CHECK(num == av[idx][1]); - idx++; + check_sum += av[i][1]; + } + + { + size_t idx = 0; + size_t sum = 0; + for (auto num : section) + { + CHECK(num == av[idx][1]); + sum += num; + idx++; + } + + CHECK(sum == check_sum); + } + { + size_t idx = length - 1; + size_t sum = 0; + for (auto iter = section.rbegin(); iter != section.rend(); ++iter) + { + CHECK(*iter == av[idx][1]); + sum += *iter; + idx--; + } + + CHECK(sum == check_sum); } } @@ -989,7 +1098,7 @@ SUITE(array_view_tests) auto bounds = strided_bounds<1>({ length }, { 2 }); #else auto bounds = strided_bounds<1>(index<1>{ length }, index<1>{ 2 }); -#endif +#endif strided_array_view strided(&av.data()[1], av.size() - 1, bounds); CHECK(strided.size() == length); @@ -1050,7 +1159,7 @@ SUITE(array_view_tests) for (unsigned int k = 0; k < section.extent<2>(); ++k) { auto idx = index<3>{ i,j,k }; // avoid braces in the CHECK macro - CHECK(section[idx] == expected[2 * i + 2 * j + k]); + CHECK(section[idx] == expected[2 * i + 2 * j + k]); } } @@ -1168,6 +1277,152 @@ SUITE(array_view_tests) } + template + index Convert(index index) + { + return{ index }; + } + + TEST(DomainConverters) + { + // to smaller + { + index<2, int> int_index{ 0,1 }; + index<2, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + CHECK(short_index[1] == 1); + } + + // to smaller (failure) + { + index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; + CHECK_THROW((Convert<2,int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + CHECK(uint_index[1] == 1); + } + + // to same, sign mismatch, reversed + { + index<2, unsigned int> uint_index{ 0,1 }; + index<2, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + CHECK(int_index[1] == 1); + } + + // to smaller, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + CHECK(ushort_index[1] == 1); + } + + // to bigger + { + index<2, int> int_index{ 0,1 }; + index<2, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + CHECK(longlong_index[1] == 1); + } + + // to bigger with max index + { + index<2, int> big_int_index{ std::numeric_limits::max(), 1 }; + index<2, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == std::numeric_limits::max()); + CHECK(longlong_index[1] == 1); + } + + // to bigger, sign mismatch + { + index<2, int> int_index{ 0,1 }; + index<2, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + CHECK(ulonglong_index[1] == 1); + } + + } + + TEST(DomainConvertersRank1) + { + // to smaller + { + index<1, int> int_index{ 0 }; + index<1, short> short_index{ int_index }; + + CHECK(short_index[0] == 0); + } + + // to smaller (failure) + { + index<1, int> big_int_index{ std::numeric_limits::max() }; + + CHECK_THROW((Convert<1, int, short int>(big_int_index)), fail_fast); + } + + // to same, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned int> uint_index{ int_index }; + + CHECK(uint_index[0] == 0); + } + + // to same, sign mismatch, reversed + { + index<1, unsigned int> uint_index{ 0 }; + index<1, int> int_index{ uint_index }; + + CHECK(int_index[0] == 0); + } + + // to smaller, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned short> ushort_index{ int_index }; + + CHECK(ushort_index[0] == 0); + } + + // to bigger + { + index<1, int> int_index{ 0 }; + index<1, long long> longlong_index{ int_index }; + + CHECK(longlong_index[0] == 0); + } + + // to bigger with max index + { + index<1, int> big_int_index{ std::numeric_limits::max() }; + index<1, long long> longlong_index{ big_int_index }; + + CHECK(longlong_index[0] == std::numeric_limits::max()); + } + + // to bigger, sign mismatch + { + index<1, int> int_index{ 0 }; + index<1, unsigned long long> ulonglong_index{ int_index }; + + CHECK(ulonglong_index[0] == 0); + } + + } + TEST(constructors) { array_view av(nullptr); @@ -1531,7 +1786,7 @@ SUITE(array_view_tests) CHECK_THROW(f(), fail_fast); } - TEST(AsWriteableBytes) + TEST(AsWriteableBytes) { int a[] = { 1, 2, 3, 4 }; @@ -1557,7 +1812,36 @@ SUITE(array_view_tests) CHECK(wav.data() == (byte*)&a[0]); CHECK(wav.length() == sizeof(a)); } + } + TEST(NonConstIterator) + { + int a[] = { 1, 2, 3, 4 }; + + { + array_view av = a; + auto wav = av.as_writeable_bytes(); + for (auto& b : wav) + { + b = byte(0); + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 0); + } + } + + { + array_view av = a; + for (auto& n : av) + { + n = 1; + } + for (size_t i = 0; i < 4; ++i) + { + CHECK(a[i] == 1); + } + } } TEST(ArrayViewComparison) diff --git a/tests/maybenull_tests.cpp b/tests/maybenull_tests.cpp deleted file mode 100644 index 74d449f..0000000 --- a/tests/maybenull_tests.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -// Copyright (c) 2015 Microsoft Corporation. All rights reserved. -// -// This code is licensed under the MIT License (MIT). -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// -/////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include -#include - -using namespace gsl; - -struct MyBase { bool foo() { return true; } }; -struct MyDerived : public MyBase {}; -struct Unrelated {}; - -SUITE(MaybeNullTests) -{ - TEST(TestMaybeNull1) - { -#ifdef CONFIRM_COMPILATION_ERRORS - // Forbid non-nullptr assignable types - maybe_null_ret> f_ret(std::vector{1}); - maybe_null_ret> f_ret(std::vector{1}); - maybe_null_ret z_ret(10); - maybe_null_dbg> y_dbg({1,2}); - maybe_null_dbg z_dbg(10); - maybe_null_dbg> y_dbg({1,2}); -#endif - int n = 5; - maybe_null_dbg opt_n(&n); - int result = 0; - bool threw = false; - - CHECK_THROW(result = *opt_n, fail_fast); - - maybe_null_ret> x_ret(std::make_shared(10)); // shared_ptr is nullptr assignable - maybe_null_dbg> x_dbg(std::make_shared(10)); // shared_ptr is nullptr assignable - } - - TEST(TestMaybeNull2) - { - int n = 5; - maybe_null opt_n(&n); - int result = 0; - if (opt_n.present()) - result = *opt_n; - } - - TEST(TestMaybeNull3) - { - int n = 5; - maybe_null opt_n(&n); - int result = 0; - if (opt_n != nullptr) - result = *opt_n; - } - - int test4_helper(maybe_null p) - { - if (p != nullptr) - return *p; - return -1; - } - - TEST(TestMaybeNull4) - { - int n = 5; - int result = 0; - result = test4_helper(&n); - } - - int test5_helper(maybe_null_dbg p) - { - return *p; - } - - TEST(TestMaybeNull5) - { - int n = 5; - int result = 0; - bool threw = false; - - CHECK_THROW(result = test5_helper(&n), fail_fast); - } - -#ifdef CONFIRM_COMPILATION_ERRORS - int TestMaybeNull6() - { - int n; - maybe_null o(n); - } -#endif - - int g_int; - void test7_helper(maybe_null *> outptr) - { - g_int = 5; - - if (outptr.present()) - *outptr = &g_int; - } - - void test7b_helper(maybe_null_dbg *> outptr) - { - g_int = 5; - - if (outptr.present()) - *outptr = &g_int; - } - - TEST(TestMaybeNull7a) - { - maybe_null outval; - test7_helper(&outval); - CHECK(outval.present() && *outval == 5); - } - - TEST(TestMaybeNull7b) - { - maybe_null_dbg outval; - test7b_helper(&outval); - CHECK_THROW((void)*outval, fail_fast); - } - - int test8_helper1(maybe_null_dbg opt) - { - return *opt; - } - - int test8_helper2a(maybe_null_dbg opt) - { - if (!opt.present()) - return 0; - return test8_helper1(opt); - } - - TEST(TestMaybeNull8a) - { - int n = 5; - maybe_null_dbg opt(&n); - CHECK_THROW(test8_helper2a(opt), fail_fast); - } - -#ifdef CONVERT_TO_PTR_TO_CONST - int test9_helper(maybe_null copt) - { - if (copt.present()) - return *copt; - return 0; - } - - void TestMaybeNull9() - { - int n = 5; - maybe_null opt(&n); - CHECK_THROW(test9_helper(opt), fail_fast); - } -#endif - - TEST(TestMaybeNullCasting) - { - MyDerived derived; - maybe_null p = &derived; - CHECK(p.present()); - - maybe_null q = p; - CHECK(q == p); - - maybe_null_dbg pdbg = &derived; - CHECK(pdbg.present()); - - maybe_null_dbg qdbg = pdbg; - CHECK(qdbg == pdbg); - -#ifdef CONFIRM_COMPILATION_ERRORS - maybe_null r = p; - maybe_null s = reinterpret_cast(p); -#endif - maybe_null_dbg t = reinterpret_cast(p.get()); - - CHECK_THROW((void)(void*)t.get(), fail_fast); - maybe_null_dbg u = reinterpret_cast(p.get()); - CHECK(u.present()); - CHECK((void*)p.get() == (void*)u.get()); - } - - TEST(TestMaybeNullArrow) - { - MyDerived derived; - maybe_null_dbg p = &derived; - - CHECK_THROW(p->foo(), fail_fast); - CHECK(p.present()); - CHECK(p->foo()); - - maybe_null q = p; - CHECK(q.present()); - CHECK(q->foo()); - } - - TEST(TestMaybeNullCompare) - { - int i1 = 1; - int i2 = 2; - - maybe_null_dbg p1 = &i1; - maybe_null_dbg p1_2 = &i1; - maybe_null_dbg p2 = &i2; - - CHECK_THROW(p1.get(), fail_fast); - CHECK_THROW(p1_2.get(), fail_fast); - CHECK_THROW(p2.get(), fail_fast); - - CHECK(p1 != p2); - CHECK(!(p1 == p2)); - CHECK(p1 == p1); - CHECK(p1 == p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - CHECK(p1_2.get() != nullptr); - CHECK(p2.get() != nullptr); - } - - TEST(TestMaybeNullCopy) - { - int i1 = 1; - int i2 = 2; - - maybe_null_dbg p1 = &i1; - maybe_null_dbg p1_2 = &i1; - maybe_null_dbg p2 = &i2; - - CHECK(p1 != p2); - CHECK(p1 == p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - CHECK(p2.get() != nullptr); - - p1 = p2; - - // Make sure we now throw - CHECK_THROW(p1.get(), fail_fast); - - CHECK(p1 == p2); - CHECK(p1 != p1_2); - - // Make sure we no longer throw here - CHECK(p1.get() != nullptr); - } - - TEST(TestMaybeNullAssignmentOps) - { - MyBase base; - MyDerived derived; - Unrelated unrelated; - - not_null nnBase(&base); - not_null nnDerived(&derived); - not_null nnUnrelated(&unrelated); - - maybe_null_ret mnBase_ret1(&base), mnBase_ret2; - mnBase_ret2 = mnBase_ret1; // maybe_null_ret = maybe_null_ret - mnBase_ret2 = nnBase; // maybe_null_ret = not_null - - maybe_null_ret mnDerived_ret(&derived); - mnBase_ret2 = mnDerived_ret; // maybe_null_ret = maybe_null_ret - mnBase_ret1 = &derived; // maybe_null_ret = U; - mnBase_ret1 = nnDerived; // maybe_null_ret = not_null - - maybe_null_ret mnUnrelated_ret; - mnUnrelated_ret = &unrelated; // maybe_null_ret = T - - maybe_null_dbg mnBase_dbg1(&base), mnBase_dbg2; - mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg2 = nnBase; // maybe_null_dbg = not_null - - maybe_null_dbg mnDerived_dbg(&derived); - mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg = maybe_null_dbg - mnBase_dbg1 = &derived; // maybe_null_dbg = U; - mnBase_dbg1 = nnDerived; // maybe_null_dbg = not_null - - maybe_null_dbg mnUnrelated_dbg; - mnUnrelated_dbg = &unrelated; // maybe_null_dbg = T - } -} - -int main(int, const char *[]) -{ - return UnitTest::RunAllTests(); -}