Conflicts:
	include/array_view.h
	tests/array_view_tests.cpp
This commit is contained in:
Neil MacIntosh 2015-10-28 16:53:53 -07:00
commit a4fa2b3bd9
4 changed files with 364 additions and 445 deletions

View File

@ -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 The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the
[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). [C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org).

View File

@ -30,6 +30,7 @@
#include <array> #include <array>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
#include <functional>
#include "fail_fast.h" #include "fail_fast.h"
#ifdef _MSC_VER #ifdef _MSC_VER
@ -68,24 +69,18 @@ namespace gsl {
*/ */
namespace details namespace details
{ {
template <typename T> template <typename SizeType>
class arrow_proxy struct SizeTypeTraits
{ {
public: static const SizeType max_value = std::numeric_limits<SizeType>::max();
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<typename... Ts>
class are_integral : public std::integral_constant<bool, true> {};
template<typename T, typename... Ts>
class are_integral<T, Ts...> : public std::integral_constant<bool, std::is_integral<T>::value && are_integral<Ts...>::value> {};
} }
template <size_t Rank> template <size_t Rank>
@ -111,14 +106,12 @@ public:
std::copy(values, values + Rank, elems); std::copy(values, values + Rank, elems);
} }
// Preconditions: il.size() == rank template<typename... Ts, bool Enabled1 = (sizeof...(Ts) == Rank), bool Enabled2 = details::are_integral<Ts...>::value, typename Dummy = std::enable_if_t<Enabled1 && Enabled2, bool>>
constexpr index(std::initializer_list<value_type> il) noexcept constexpr index(Ts... ds) noexcept : elems{ static_cast<value_type>(ds)... }
{ {}
fail_fast_assert(il.size() == Rank, "The size of the initializer list must match the rank of the array");
std::copy(begin(il), end(il), elems);
}
constexpr index(const index& other) noexcept = default; constexpr index(const index& other) noexcept = default;
constexpr index& operator=(const index& rhs) noexcept = default; constexpr index& operator=(const index& rhs) noexcept = default;
// Preconditions: component_idx < rank // Preconditions: component_idx < rank
@ -218,144 +211,46 @@ private:
value_type elems[Rank] = {}; value_type elems[Rank] = {};
}; };
template<> #ifndef _MSC_VER
class index<1>
struct static_bounds_dynamic_range_t
{ {
public: template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
static const size_t rank = 1; constexpr operator T() const noexcept
using size_type = std::ptrdiff_t;
using value_type = std::ptrdiff_t;
using reference = std::add_lvalue_reference_t<std::ptrdiff_t>;
using const_reference = const std::ptrdiff_t&;//std::add_const_t<std::add_lvalue_reference_t<std::ptrdiff_t>>;
template <size_t>
friend class index;
constexpr index() noexcept : value(0)
{}
constexpr index(value_type e) noexcept : value(e)
{}
constexpr index(const value_type(&values)[1]) noexcept : index(values[0])
{}
constexpr index(const index &) noexcept = default;
// Preconditions: component_idx < rank
constexpr reference operator[](size_type component_idx) noexcept
{ {
fail_fast_assert(component_idx == 0, "Component index must be less than rank"); return static_cast<T>(-1);
return value;
} }
// Preconditions: component_idx < rank template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
constexpr const_reference operator[](size_type component_idx) const noexcept constexpr bool operator ==(T other) const noexcept
{ {
fail_fast_assert(component_idx == 0, "Component index must be less than rank"); return static_cast<T>(-1) == other;
return value;
} }
constexpr bool operator==(const index& rhs) const noexcept template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
constexpr bool operator !=(T other) const noexcept
{ {
return value == rhs.value; return static_cast<T>(-1) != other;
} }
constexpr bool operator!=(const index& rhs) const noexcept
{
return !(*this == rhs);
}
constexpr index operator+() const noexcept
{
return *this;
}
constexpr index operator-() const noexcept
{
return index(-value);
}
constexpr index operator+(const index& rhs) const noexcept
{
return index(value + rhs.value);
}
constexpr index operator-(const index& rhs) const noexcept
{
return index(value - rhs.value);
}
constexpr index& operator+=(const index& rhs) noexcept
{
value += rhs.value;
return *this;
}
constexpr index& operator-=(const index& rhs) noexcept
{
value -= rhs.value;
return *this;
}
constexpr index& operator++() noexcept
{
++value;
return *this;
}
constexpr index operator++(int) noexcept
{
index ret = *this;
++(*this);
return ret;
}
constexpr index& operator--() noexcept
{
--value;
return *this;
}
constexpr index operator--(int) noexcept
{
index ret = *this;
--(*this);
return ret;
}
constexpr index operator*(value_type v) const noexcept
{
return index(value * v);
}
constexpr index operator/(value_type v) const noexcept
{
return index(value / v);
}
constexpr index& operator*=(value_type v) noexcept
{
value *= v;
return *this;
}
constexpr index& operator/=(value_type v) noexcept
{
value /= v;
return *this;
}
friend constexpr index operator*(value_type v, const index& rhs) noexcept
{
return{ rhs * v };
}
private:
value_type value;
}; };
template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
constexpr bool operator ==(T left, static_bounds_dynamic_range_t right) noexcept
{
return right == left;
}
template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
constexpr bool operator !=(T left, static_bounds_dynamic_range_t right) noexcept
{
return right != left;
}
constexpr static_bounds_dynamic_range_t dynamic_range{};
#else
const std::ptrdiff_t dynamic_range = -1; const std::ptrdiff_t dynamic_range = -1;
#endif
struct generalized_mapping_tag {}; struct generalized_mapping_tag {};
struct contiguous_mapping_tag : generalized_mapping_tag {}; struct contiguous_mapping_tag : generalized_mapping_tag {};
@ -652,12 +547,13 @@ public:
using size_type = std::ptrdiff_t; using size_type = std::ptrdiff_t;
using index_type = index<rank>; using index_type = index<rank>;
using iterator = bounds_iterator<index_type>; using const_index_type = std::add_const_t<index_type>;
using const_iterator = bounds_iterator<index_type>; using iterator = bounds_iterator<const_index_type>;
using difference_type = std::ptrdiff_t; using const_iterator = bounds_iterator<const_index_type>;
using difference_type = std::ptrdiff_t;
using sliced_type = static_bounds<RestRanges...>; using sliced_type = static_bounds<RestRanges...>;
using mapping_type = contiguous_mapping_tag; using mapping_type = contiguous_mapping_tag;
public:
constexpr static_bounds(const static_bounds&) = default; constexpr static_bounds(const static_bounds&) = default;
template <std::ptrdiff_t... Ranges, typename Dummy = std::enable_if_t< template <std::ptrdiff_t... Ranges, typename Dummy = std::enable_if_t<
@ -667,7 +563,7 @@ public:
constexpr static_bounds(std::initializer_list<size_type> il) : m_ranges(il.begin()) constexpr static_bounds(std::initializer_list<size_type> il) : m_ranges(il.begin())
{ {
fail_fast_assert(MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array"); fail_fast_assert((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || MyRanges::DynamicNum == il.size(), "Size of the initializer list must match the rank of the array");
fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type"); fail_fast_assert(m_ranges.totalSize() <= PTRDIFF_MAX, "Size of the range is larger than the max element of the size type");
} }
@ -742,7 +638,7 @@ public:
constexpr const_iterator begin() const noexcept constexpr const_iterator begin() const noexcept
{ {
return const_iterator(*this); return const_iterator(*this, index_type{});
} }
constexpr const_iterator end() const noexcept constexpr const_iterator end() const noexcept
@ -765,9 +661,10 @@ public:
using size_type = value_type; using size_type = value_type;
using difference_type = value_type; using difference_type = value_type;
using index_type = index<rank>; using index_type = index<rank>;
using iterator = bounds_iterator<index_type>; using const_index_type = std::add_const_t<index_type>;
using const_iterator = bounds_iterator<index_type>; using iterator = bounds_iterator<const_index_type>;
static const size_t dynamic_rank = rank; using const_iterator = bounds_iterator<const_index_type>;
static const value_type dynamic_rank = rank;
static const value_type static_size = dynamic_range; static const value_type static_size = dynamic_range;
using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>; using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
using mapping_type = generalized_mapping_tag; using mapping_type = generalized_mapping_tag;
@ -850,13 +747,12 @@ public:
{ {
return m_extents; return m_extents;
} }
constexpr const_iterator begin() const noexcept
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() }; return const_iterator{ *this, index_bounds() };
} }
@ -874,15 +770,11 @@ template <size_t Rank>
struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true> {}; struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true> {};
template <typename IndexType> template <typename IndexType>
class bounds_iterator class bounds_iterator: public std::iterator<std::random_access_iterator_tag, IndexType>
: public std::iterator<std::random_access_iterator_tag,
IndexType,
ptrdiff_t,
const details::arrow_proxy<IndexType>,
const IndexType>
{ {
private: private:
using Base = std::iterator <std::random_access_iterator_tag, IndexType, ptrdiff_t, const details::arrow_proxy<IndexType>, const IndexType>; using Base = std::iterator <std::random_access_iterator_tag, IndexType>;
public: public:
static const size_t rank = IndexType::rank; static const size_t rank = IndexType::rank;
using typename Base::reference; using typename Base::reference;
@ -892,79 +784,88 @@ public:
using index_type = value_type; using index_type = value_type;
using index_size_type = typename IndexType::value_type; using index_size_type = typename IndexType::value_type;
template <typename Bounds> template <typename Bounds>
explicit bounds_iterator(const Bounds& bnd, value_type curr = value_type{}) noexcept explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept
: boundary(bnd.index_bounds()) : boundary(bnd.index_bounds()), curr(std::move(curr))
, curr(std::move(curr))
{ {
static_assert(is_bounds<Bounds>::value, "Bounds type must be provided"); static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
} }
reference operator*() const noexcept
constexpr reference operator*() const noexcept
{ {
return curr; return curr;
} }
pointer operator->() const noexcept
constexpr pointer operator->() const noexcept
{ {
return details::arrow_proxy<value_type>{ curr }; return &curr;
} }
bounds_iterator& operator++() noexcept
constexpr bounds_iterator& operator++() noexcept
{ {
for (size_t i = rank; i-- > 0;) for (size_t i = rank; i-- > 0;)
{ {
if (++curr[i] < boundary[i]) if (curr[i] < boundary[i] - 1)
{ {
curr[i]++;
return *this; return *this;
} }
else curr[i] = 0;
{
curr[i] = 0;
}
} }
// If we're here we've wrapped over - set to past-the-end. // If we're here we've wrapped over - set to past-the-end.
for (size_t i = 0; i < rank; ++i) curr = boundary;
{
curr[i] = boundary[i];
}
return *this; return *this;
} }
bounds_iterator operator++(int) noexcept
constexpr bounds_iterator operator++(int) noexcept
{ {
auto ret = *this; auto ret = *this;
++(*this); ++(*this);
return ret; return ret;
} }
bounds_iterator& operator--() noexcept
constexpr bounds_iterator& operator--() noexcept
{ {
for (size_t i = rank; i-- > 0;) if (!less(curr, boundary))
{ {
if (curr[i]-- > 0) // if at the past-the-end, set to last element
{ for (size_t i = 0; i < rank; ++i)
return *this;
}
else
{ {
curr[i] = boundary[i] - 1; curr[i] = boundary[i] - 1;
} }
return *this;
}
for (size_t i = rank; i-- > 0;)
{
if (curr[i] >= 1)
{
curr[i]--;
return *this;
}
curr[i] = boundary[i] - 1;
} }
// If we're here the preconditions were violated // If we're here the preconditions were violated
// "pre: there exists s such that r == ++s" // "pre: there exists s such that r == ++s"
fail_fast_assert(false); fail_fast_assert(false);
return *this; return *this;
} }
bounds_iterator operator--(int) noexcept
constexpr bounds_iterator operator--(int) noexcept
{ {
auto ret = *this; auto ret = *this;
--(*this); --(*this);
return ret; return ret;
} }
bounds_iterator operator+(difference_type n) const noexcept
constexpr bounds_iterator operator+(difference_type n) const noexcept
{ {
bounds_iterator ret{ *this }; bounds_iterator ret{ *this };
return ret += n; return ret += n;
} }
bounds_iterator& operator+=(difference_type n) noexcept
constexpr bounds_iterator& operator+=(difference_type n) noexcept
{ {
auto linear_idx = linearize(curr) + n; auto linear_idx = linearize(curr) + n;
value_type stride; std::remove_const_t<value_type> stride;
stride[rank - 1] = 1; stride[rank - 1] = 1;
for (size_t i = rank - 1; i-- > 0;) for (size_t i = rank - 1; i-- > 0;)
{ {
@ -975,76 +876,84 @@ public:
curr[i] = linear_idx / stride[i]; curr[i] = linear_idx / stride[i];
linear_idx = 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; return *this;
} }
bounds_iterator operator-(difference_type n) const noexcept
constexpr bounds_iterator operator-(difference_type n) const noexcept
{ {
bounds_iterator ret{ *this }; bounds_iterator ret{ *this };
return ret -= n; return ret -= n;
} }
bounds_iterator& operator-=(difference_type n) noexcept
constexpr bounds_iterator& operator-=(difference_type n) noexcept
{ {
return *this += -n; 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); return linearize(curr) - linearize(rhs.curr);
} }
reference operator[](difference_type n) const noexcept
constexpr value_type operator[](difference_type n) const noexcept
{ {
return *(*this + n); return *(*this + n);
} }
bool operator==(const bounds_iterator& rhs) const noexcept
constexpr bool operator==(const bounds_iterator& rhs) const noexcept
{ {
return curr == rhs.curr; return curr == rhs.curr;
} }
bool operator!=(const bounds_iterator& rhs) const noexcept
constexpr bool operator!=(const bounds_iterator& rhs) const noexcept
{ {
return !(*this == rhs); 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) return less(curr, rhs.curr);
{
if (curr[i] < rhs.curr[i])
return true;
}
return false;
} }
bool operator<=(const bounds_iterator& rhs) const noexcept
constexpr bool operator<=(const bounds_iterator& rhs) const noexcept
{ {
return !(rhs < *this); return !(rhs < *this);
} }
bool operator>(const bounds_iterator& rhs) const noexcept
constexpr bool operator>(const bounds_iterator& rhs) const noexcept
{ {
return rhs < *this; return rhs < *this;
} }
bool operator>=(const bounds_iterator& rhs) const noexcept
constexpr bool operator>=(const bounds_iterator& rhs) const noexcept
{ {
return !(rhs > *this); return !(rhs > *this);
} }
void swap(bounds_iterator& rhs) noexcept void swap(bounds_iterator& rhs) noexcept
{ {
std::swap(boundary, rhs.boundary); std::swap(boundary, rhs.boundary);
std::swap(curr, rhs.curr); std::swap(curr, rhs.curr);
} }
private: 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. // TODO: Smarter impl.
// Check if past-the-end // 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 multiplier = 1;
index_size_type res = 0; index_size_type res = 0;
if (pte) if (!less(idx, boundary))
{ {
res = 1; res = 1;
for (size_t i = rank; i-- > 0;) for (size_t i = rank; i-- > 0;)
@ -1063,119 +972,9 @@ private:
} }
return res; return res;
} }
value_type boundary; value_type boundary;
value_type curr; std::remove_const_t<value_type> curr;
};
template <>
class bounds_iterator<index<1>>
: public std::iterator<std::random_access_iterator_tag,
index<1>,
ptrdiff_t,
const details::arrow_proxy<index<1>>,
const index<1>>
{
using Base = std::iterator<std::random_access_iterator_tag, index<1>, std::ptrdiff_t, const details::arrow_proxy<index<1>>, const index<1>>;
public:
using Base::reference;
using Base::pointer;
using Base::difference_type;
using Base::value_type;
using index_type = value_type;
using index_size_type = index_type::size_type;
template <typename Bounds>
explicit bounds_iterator(const Bounds&, value_type curr = value_type{}) noexcept
: curr( std::move(curr) )
{}
reference operator*() const noexcept
{
return curr;
}
pointer operator->() const noexcept
{
return details::arrow_proxy<value_type>{ curr };
}
bounds_iterator& operator++() noexcept
{
++curr;
return *this;
}
bounds_iterator operator++(int) noexcept
{
auto ret = *this;
++(*this);
return ret;
}
bounds_iterator& operator--() noexcept
{
curr--;
return *this;
}
bounds_iterator operator--(int) noexcept
{
auto ret = *this;
--(*this);
return ret;
}
bounds_iterator operator+(difference_type n) const noexcept
{
bounds_iterator ret{ *this };
return ret += n;
}
bounds_iterator& operator+=(difference_type n) noexcept
{
curr += n;
return *this;
}
bounds_iterator operator-(difference_type n) const noexcept
{
bounds_iterator ret{ *this };
return ret -= n;
}
bounds_iterator& operator-=(difference_type n) noexcept
{
return *this += -n;
}
difference_type operator-(const bounds_iterator& rhs) const noexcept
{
return curr[0] - rhs.curr[0];
}
reference operator[](difference_type n) const noexcept
{
return curr + n;
}
bool operator==(const bounds_iterator& rhs) const noexcept
{
return curr == rhs.curr;
}
bool operator!=(const bounds_iterator& rhs) const noexcept
{
return !(*this == rhs);
}
bool operator<(const bounds_iterator& rhs) const noexcept
{
return curr[0] < rhs.curr[0];
}
bool operator<=(const bounds_iterator& rhs) const noexcept
{
return !(rhs < *this);
}
bool operator>(const bounds_iterator& rhs) const noexcept
{
return rhs < *this;
}
bool operator>=(const bounds_iterator& rhs) const noexcept
{
return !(rhs > *this);
}
void swap(bounds_iterator& rhs) noexcept
{
std::swap(curr, rhs.curr);
}
private:
value_type curr;
}; };
template <typename IndexType> template <typename IndexType>
@ -1237,10 +1036,11 @@ public:
using size_type = typename bounds_type::size_type; using size_type = typename bounds_type::size_type;
using index_type = typename bounds_type::index_type; using index_type = typename bounds_type::index_type;
using value_type = ValueType; using value_type = ValueType;
using const_value_type = std::add_const_t<value_type>;
using pointer = ValueType*; using pointer = ValueType*;
using reference = ValueType&; using reference = ValueType&;
using iterator = std::conditional_t<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view>, general_array_view_iterator<basic_array_view>>; using iterator = std::conditional_t<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view>, general_array_view_iterator<basic_array_view>>;
using const_iterator = std::conditional_t<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view<const ValueType, BoundsType>>, general_array_view_iterator<basic_array_view<const ValueType, BoundsType>>>; using const_iterator = std::conditional_t<std::is_same<typename BoundsType::mapping_type, contiguous_mapping_tag>::value, contiguous_array_view_iterator<basic_array_view<const_value_type, BoundsType>>, general_array_view_iterator<basic_array_view<const_value_type, BoundsType>>>;
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using sliced_type = std::conditional_t<rank == 1, value_type, basic_array_view<value_type, typename BoundsType::sliced_type>>; using sliced_type = std::conditional_t<rank == 1, value_type, basic_array_view<value_type, typename BoundsType::sliced_type>>;
@ -1293,7 +1093,7 @@ public:
} }
constexpr iterator end() const constexpr iterator end() const
{ {
return iterator {this}; return iterator {this, false};
} }
constexpr const_iterator cbegin() const constexpr const_iterator cbegin() const
{ {
@ -1301,7 +1101,7 @@ public:
} }
constexpr const_iterator cend() const constexpr const_iterator cend() const
{ {
return const_iterator {reinterpret_cast<const basic_array_view<const value_type, bounds_type> *>(this)}; return const_iterator {reinterpret_cast<const basic_array_view<const value_type, bounds_type> *>(this), false};
} }
constexpr reverse_iterator rbegin() const constexpr reverse_iterator rbegin() const
@ -1557,9 +1357,9 @@ public:
// from n-dimensions static array with size // from n-dimensions static array with size
template <typename T, size_t N, typename Helper = details::ArrayViewArrayTraits<T, N>, template <typename T, size_t N, typename Helper = details::ArrayViewArrayTraits<T, N>,
typename Dummy = std::enable_if_t<std::is_convertible<Helper::value_type(*)[], typename Base::value_type(*)[]>::value> typename = std::enable_if_t<std::is_convertible<Helper::value_type(*)[], typename Base::value_type(*)[]>::value>
> >
constexpr array_view(T(&arr)[N], size_type size) : Base(arr, typename Helper::bounds_type{ size }) constexpr array_view(T(&arr)[N], size_type size) : Base(arr, Helper::bounds_type{size})
{ {
fail_fast_assert(size <= N); fail_fast_assert(size <= N);
} }
@ -1608,10 +1408,9 @@ public:
{} {}
// reshape // reshape
template <typename... Dimensions2> template <typename... Dimensions2, typename = std::enable_if_t<(sizeof...(Dimensions2) > 0)>>
constexpr array_view<ValueType, Dimensions2::value...> as_array_view(Dimensions2... dims) constexpr array_view<ValueType, Dimensions2::value...> as_array_view(Dimensions2... dims)
{ {
static_assert(sizeof...(Dimensions2) > 0, "the target array_view must have at least one dimension.");
using BoundsType = typename array_view<ValueType, (Dimensions2::value)...>::bounds_type; using BoundsType = typename array_view<ValueType, (Dimensions2::value)...>::bounds_type;
auto tobounds = details::static_as_array_view_helper<BoundsType>(dims..., details::Sep{}); auto tobounds = details::static_as_array_view_helper<BoundsType>(dims..., details::Sep{});
details::verifyBoundsReshape(this->bounds(), tobounds); details::verifyBoundsReshape(this->bounds(), tobounds);
@ -1635,7 +1434,7 @@ public:
// from bytes array // from bytes array
template<typename U, bool IsByte = std::is_same<value_type, const byte>::value, typename = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>> template<typename U, bool IsByte = std::is_same<value_type, const byte>::value, typename = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>>
constexpr auto as_array_view() const noexcept -> array_view<const U, dynamic_range, (Base::bounds_type::static_size != dynamic_range ? static_cast<size_t>(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> constexpr auto as_array_view() const noexcept -> array_view<const U, (Base::bounds_type::static_size != dynamic_range ? static_cast<size_t>(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)>
{ {
static_assert(std::is_standard_layout<U>::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast<size_type>(sizeof(U)) == 0), static_assert(std::is_standard_layout<U>::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast<size_type>(sizeof(U)) == 0),
"Target type must be standard layout and its size must match the byte array size"); "Target type must be standard layout and its size must match the byte array size");
@ -1643,8 +1442,8 @@ public:
return { reinterpret_cast<const U*>(this->data()), this->bytes() / static_cast<size_type>(sizeof(U)) }; return { reinterpret_cast<const U*>(this->data()), this->bytes() / static_cast<size_type>(sizeof(U)) };
} }
template<typename U, bool IsByte = std::is_same<value_type, byte>::value, typename Dummy = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>> template<typename U, bool IsByte = std::is_same<value_type, byte>::value, typename = std::enable_if_t<IsByte && sizeof...(RestDimensions) == 0>>
constexpr auto as_array_view() const noexcept -> array_view<U, dynamic_range, (Base::bounds_type::static_size != dynamic_range ? static_cast<size_t>(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)> constexpr auto as_array_view() const noexcept -> array_view<U, (Base::bounds_type::static_size != dynamic_range ? static_cast<size_t>(Base::bounds_type::static_size) / sizeof(U) : dynamic_range)>
{ {
static_assert(std::is_standard_layout<U>::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast<size_t>(sizeof(U)) == 0), static_assert(std::is_standard_layout<U>::value && (Base::bounds_type::static_size == dynamic_range || Base::bounds_type::static_size % static_cast<size_t>(sizeof(U)) == 0),
"Target type must be standard layout and its size must match the byte array size"); "Target type must be standard layout and its size must match the byte array size");
@ -1925,10 +1724,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"); 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) :
contiguous_array_view_iterator (const ArrayView *container, bool isbegin = false) : m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) {}
m_pdata(isbegin ? container->m_pdata : container->m_pdata + container->size()), m_validator(container) { }
public: public:
reference operator*() const noexcept reference operator*() const noexcept
{ {
@ -2044,17 +1841,15 @@ private:
const ArrayView * m_container; const ArrayView * m_container;
typename ArrayView::bounds_type::iterator m_itr; typename ArrayView::bounds_type::iterator m_itr;
general_array_view_iterator(const ArrayView *container, bool isbegin) :
general_array_view_iterator(const ArrayView *container, bool isbegin = false) : m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end())
m_container(container), m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) {}
{}
public: public:
reference operator*() const noexcept reference operator*() noexcept
{ {
return (*m_container)[*m_itr]; return (*m_container)[*m_itr];
} }
pointer operator->() const noexcept pointer operator->() noexcept
{ {
return &(*m_container)[*m_itr]; return &(*m_container)[*m_itr];
} }

View File

@ -197,6 +197,19 @@ private:
} // namespace gsl } // namespace gsl
namespace std
{
template<class T>
struct hash<gsl::not_null<T>>
{
size_t operator()(const gsl::not_null<T> & value) const
{
return hash<T>{}(value);
}
};
} // namespace std
#ifdef _MSC_VER #ifdef _MSC_VER
#undef constexpr #undef constexpr

View File

@ -16,16 +16,11 @@
#include <UnitTest++/UnitTest++.h> #include <UnitTest++/UnitTest++.h>
#include <array_view.h> #include <array_view.h>
#include <numeric>
#include <limits>
#include <array>
#include <string> #include <string>
#include <vector> #include <vector>
#include <list> #include <list>
#include <iostream> #include <iostream>
#include <functional>
#include <algorithm>
using namespace std; using namespace std;
using namespace gsl; using namespace gsl;
@ -408,71 +403,71 @@ SUITE(array_view_tests)
array_view<const int, 2> av2{ av }; array_view<const int, 2> av2{ av };
CHECK(av2[1] == 5); CHECK(av2[1] == 5);
static_assert(std::is_convertible<const array_view<int, 2>, array_view<const int, 2>>::value, "ctor is not implicit!"); // static_assert(std::is_convertible<const array_view<int, 2>, array_view<const int, 2>>::value, "ctor is not implicit!");
//
const strided_array_view<int, 1> src{ arr, {2, 1} }; // const strided_array_view<int, 1> src{ arr, {2, 1} };
strided_array_view<const int, 1> sav{ src }; // strided_array_view<const int, 1> sav{ src };
CHECK(sav.bounds().index_bounds() == index<1>{ 2 }); // CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav.bounds().stride() == 1); // CHECK(sav.bounds().stride() == 1);
CHECK(sav[1] == 5); // CHECK(sav[1] == 5);
//
static_assert(std::is_convertible<const strided_array_view<int, 1>, strided_array_view<const int, 1>>::value, "ctor is not implicit!"); // static_assert(std::is_convertible<const strided_array_view<int, 1>, strided_array_view<const int, 1>>::value, "ctor is not implicit!");
}
// Check copy constructor
{
int arr1[2] = { 3, 4 };
const strided_array_view<int, 1> src1{ arr1, {2, 1} };
strided_array_view<int, 1> sav1{ src1 };
CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav1.bounds().stride() == 1);
CHECK(sav1[0] == 3);
int arr2[6] = { 1, 2, 3, 4, 5, 6 };
const strided_array_view<const int, 2> src2{ arr2, {{ 3, 2 }, { 2, 1 }} };
strided_array_view<const int, 2> sav2{ src2 };
CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
}
// Check const-casting assignment operator
{
int arr1[2] = { 1, 2 };
int arr2[6] = { 3, 4, 5, 6, 7, 8 };
const strided_array_view<int, 1> src{ arr1, {{2}, {1}} };
strided_array_view<const int, 1> sav{ arr2, {{3}, {2}} };
strided_array_view<const int, 1>& sav_ref = (sav = src);
CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav.bounds().strides() == index<1>{ 1 });
CHECK(sav[0] == 1);
CHECK(&sav_ref == &sav);
}
// Check copy assignment operator
{
int arr1[2] = { 3, 4 };
int arr1b[1] = { 0 };
const strided_array_view<int, 1> src1{ arr1, {2, 1} };
strided_array_view<int, 1> sav1{ arr1b, {1, 1} };
strided_array_view<int, 1>& sav1_ref = (sav1 = src1);
CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
CHECK(sav1.bounds().strides() == index<1>{ 1 });
CHECK(sav1[0] == 3);
CHECK(&sav1_ref == &sav1);
const int arr2[6] = { 1, 2, 3, 4, 5, 6 };
const int arr2b[1] = { 0 };
const strided_array_view<const int, 2> src2{ arr2, {{ 3, 2 },{ 2, 1 }} };
strided_array_view<const int, 2> sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} };
strided_array_view<const int, 2>& sav2_ref = (sav2 = src2);
CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
CHECK(&sav2_ref == &sav2);
} }
//
// // Check copy constructor
// {
// int arr1[2] = { 3, 4 };
// const strided_array_view<int, 1> src1{ arr1, {2, 1} };
// strided_array_view<int, 1> sav1{ src1 };
//
// CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
// CHECK(sav1.bounds().stride() == 1);
// CHECK(sav1[0] == 3);
//
// int arr2[6] = { 1, 2, 3, 4, 5, 6 };
// const strided_array_view<const int, 2> src2{ arr2, {{ 3, 2 }, { 2, 1 }} };
// strided_array_view<const int, 2> sav2{ src2 };
// CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
// CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
// CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
// }
//
// // Check const-casting assignment operator
// {
// int arr1[2] = { 1, 2 };
// int arr2[6] = { 3, 4, 5, 6, 7, 8 };
//
// const strided_array_view<int, 1> src{ arr1, {{2}, {1}} };
// strided_array_view<const int, 1> sav{ arr2, {{3}, {2}} };
// strided_array_view<const int, 1>& sav_ref = (sav = src);
// CHECK(sav.bounds().index_bounds() == index<1>{ 2 });
// CHECK(sav.bounds().strides() == index<1>{ 1 });
// CHECK(sav[0] == 1);
// CHECK(&sav_ref == &sav);
// }
//
// // Check copy assignment operator
// {
// int arr1[2] = { 3, 4 };
// int arr1b[1] = { 0 };
// const strided_array_view<int, 1> src1{ arr1, {2, 1} };
// strided_array_view<int, 1> sav1{ arr1b, {1, 1} };
// strided_array_view<int, 1>& sav1_ref = (sav1 = src1);
// CHECK(sav1.bounds().index_bounds() == index<1>{ 2 });
// CHECK(sav1.bounds().strides() == index<1>{ 1 });
// CHECK(sav1[0] == 3);
// CHECK(&sav1_ref == &sav1);
//
// const int arr2[6] = { 1, 2, 3, 4, 5, 6 };
// const int arr2b[1] = { 0 };
// const strided_array_view<const int, 2> src2{ arr2, {{ 3, 2 },{ 2, 1 }} };
// strided_array_view<const int, 2> sav2{ arr2b, {{ 1, 1 },{ 1, 1 }} };
// strided_array_view<const int, 2>& sav2_ref = (sav2 = src2);
// CHECK((sav2.bounds().index_bounds() == index<2>{ 3, 2 }));
// CHECK((sav2.bounds().strides() == index<2>{ 2, 1 }));
// CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
// CHECK(&sav2_ref == &sav2);
// }
} }
TEST(strided_array_view_slice) TEST(strided_array_view_slice)
@ -640,23 +635,14 @@ SUITE(array_view_tests)
index<1> index{ 0, 1 }; index<1> index{ 0, 1 };
strided_array_view<int, 1> sav8{ arr,{ 1,{ 1,1 } } }; strided_array_view<int, 1> sav8{ arr,{ 1,{ 1,1 } } };
#ifdef _MSC_VER
strided_array_view<int, 1> sav9{ arr,{ { 1,1 },{ 1,1 } } }; strided_array_view<int, 1> sav9{ arr,{ { 1,1 },{ 1,1 } } };
#endif
strided_array_view<int, 1> sav10{ av,{ 1,{ 1,1 } } }; strided_array_view<int, 1> sav10{ av,{ 1,{ 1,1 } } };
#ifdef _MSC_VER
strided_array_view<int, 1> sav11{ av,{ { 1,1 },{ 1,1 } } }; strided_array_view<int, 1> sav11{ av,{ { 1,1 },{ 1,1 } } };
#endif strided_array_view<int, 2> sav12{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1 } } };
strided_array_view<int, 2> sav13{ av.as_array_view(dim<2>(), dim<2>()),{ { 1 },{ 1,1,1 } } };
strided_array_view<int, 2> sav14{ av.as_array_view(dim<2>(), dim<2>()),{ { 1,1,1 },{ 1 } } };
} }
#endif #endif
{
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1}} }), fail_fast);
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1}, {1,1,1}} }), fail_fast);
#ifdef _MSC_VER
CHECK_THROW((strided_array_view<int, 2>{ av.as_array_view(dim<2>(), dim<2>()), {{1,1,1}, {1}} }), fail_fast);
#endif
}
} }
TEST(strided_array_view_type_conversion) TEST(strided_array_view_type_conversion)
@ -840,6 +826,78 @@ SUITE(array_view_tests)
delete[] arr; delete[] arr;
} }
TEST(index_constructors)
{
{
// components of the same type
index<3> i1(0, 1, 2);
CHECK(i1[0] == 0);
// components of different types
size_t c0 = 0;
size_t c1 = 1;
index<3> i2(c0, c1, 2);
CHECK(i2[0] == 0);
// from array
index<3> i3 = { 0,1,2 };
CHECK(i3[0] == 0);
// from other index of the same size type
index<3> i4 = i3;
CHECK(i4[0] == 0);
// default
index<3> i7;
CHECK(i7[0] == 0);
// default
index<3> i9 = {};
CHECK(i9[0] == 0);
}
{
// components of the same type
index<1> i1(0);
CHECK(i1[0] == 0);
// components of different types
size_t c0 = 0;
index<1> i2(c0);
CHECK(i2[0] == 0);
// from array
index<1> i3 = { 0 };
CHECK(i3[0] == 0);
// from int
index<1> i4 = 0;
CHECK(i4[0] == 0);
// from other index of the same size type
index<1> i5 = i3;
CHECK(i5[0] == 0);
// default
index<1> i8;
CHECK(i8[0] == 0);
// default
index<1> i9 = {};
CHECK(i9[0] == 0);
}
#ifdef CONFIRM_COMPILATION_ERRORS
{
index<3> i1(0, 1);
index<3> i2(0, 1, 2, 3);
index<3> i3 = { 0 };
index<3> i4 = { 0, 1, 2, 3 };
index<1> i5 = { 0,1 };
}
#endif
}
TEST(index_operations) TEST(index_operations)
{ {
ptrdiff_t a[3] = { 0, 1, 2 }; ptrdiff_t a[3] = { 0, 1, 2 };
@ -926,11 +984,35 @@ SUITE(array_view_tests)
} }
} }
ptrdiff_t idx = 0; size_t check_sum = 0;
for (auto num : section) for (auto i = 0; i < length; ++i)
{ {
CHECK(num == av[idx][1]); check_sum += av[i][1];
idx++; }
{
auto idx = 0;
size_t sum = 0;
for (auto num : section)
{
CHECK(num == av[idx][1]);
sum += num;
idx++;
}
CHECK(sum == check_sum);
}
{
size_t idx = length - 1;
size_t sum = 0;
for (auto iter = section.rbegin(); iter != section.rend(); ++iter)
{
CHECK(*iter == av[idx][1]);
sum += *iter;
idx--;
}
CHECK(sum == check_sum);
} }
} }
@ -955,7 +1037,7 @@ SUITE(array_view_tests)
} }
// both bounds are dynamic // both bounds are dynamic
{ {
array_view<int, dynamic_range, dynamic_range> av(arr, 4); array_view<int, dynamic_range, dynamic_range> av = arr;
iterate_second_column(av); iterate_second_column(av);
} }
} }
@ -1569,7 +1651,36 @@ SUITE(array_view_tests)
CHECK(wav.data() == (byte*)&a[0]); CHECK(wav.data() == (byte*)&a[0]);
CHECK(wav.length() == sizeof(a)); CHECK(wav.length() == sizeof(a));
} }
}
TEST(NonConstIterator)
{
int a[] = { 1, 2, 3, 4 };
{
array_view<int, dynamic_range> 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<int, dynamic_range> av = a;
for (auto& n : av)
{
n = 1;
}
for (size_t i = 0; i < 4; ++i)
{
CHECK(a[i] == 1);
}
}
} }
TEST(ArrayViewComparison) TEST(ArrayViewComparison)