1/////////////////////////////////////////////////////////////////////////////// 2// 3// Copyright (c) 2015 Microsoft Corporation. All rights reserved. 4// 5// This code is licensed under the MIT License (MIT). 6// 7// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 8// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 9// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 10// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 12// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 13// THE SOFTWARE. 14// 15/////////////////////////////////////////////////////////////////////////////// 16 17#pragma once 18 19#ifndef GSL_MULTI_SPAN_H 20#define GSL_MULTI_SPAN_H 21 22#include <gsl/gsl_assert> 23#include <gsl/gsl_byte> 24#include <gsl/gsl_util> 25 26#include <algorithm> 27#include <array> 28#include <cassert> 29#include <cstddef> 30#include <cstdint> 31#include <functional> 32#include <iterator> 33#include <limits> 34#include <new> 35#include <numeric> 36#include <stdexcept> 37#include <type_traits> 38#include <utility> 39 40#ifdef _MSC_VER 41 42// turn off some warnings that are noisy about our Expects statements 43#pragma warning(push) 44#pragma warning(disable : 4127) // conditional expression is constant 45#pragma warning(disable : 4702) // unreachable code 46 47#if _MSC_VER < 1910 48#pragma push_macro("constexpr") 49#define constexpr /*constexpr*/ 50 51#endif // _MSC_VER < 1910 52#endif // _MSC_VER 53 54#ifdef GSL_THROW_ON_CONTRACT_VIOLATION 55#define GSL_NOEXCEPT /*noexcept*/ 56#else 57#define GSL_NOEXCEPT noexcept 58#endif // GSL_THROW_ON_CONTRACT_VIOLATION 59 60namespace gsl 61{ 62 63/* 64** begin definitions of index and bounds 65*/ 66namespace details 67{ 68 template <typename SizeType> 69 struct SizeTypeTraits 70 { 71 static const SizeType max_value = std::numeric_limits<SizeType>::max(); 72 }; 73 74 template <typename... Ts> 75 class are_integral : public std::integral_constant<bool, true> 76 { 77 }; 78 79 template <typename T, typename... Ts> 80 class are_integral<T, Ts...> 81 : public std::integral_constant<bool, 82 std::is_integral<T>::value && are_integral<Ts...>::value> 83 { 84 }; 85} 86 87template <std::size_t Rank> 88class index final 89{ 90 static_assert(Rank > 0, "Rank must be greater than 0!"); 91 92 template <std::size_t OtherRank> 93 friend class index; 94 95public: 96 static const std::size_t rank = Rank; 97 using value_type = std::ptrdiff_t; 98 using size_type = value_type; 99 using reference = std::add_lvalue_reference_t<value_type>; 100 using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>; 101 102 constexpr index() GSL_NOEXCEPT {} 103 104 constexpr index(const value_type (&values)[Rank]) GSL_NOEXCEPT 105 { 106 std::copy(values, values + Rank, elems); 107 } 108 109 template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) && 110 details::are_integral<Ts...>::value>> 111 constexpr index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast<value_type>(ds)...} 112 { 113 } 114 115 constexpr index(const index& other) GSL_NOEXCEPT = default; 116 117 constexpr index& operator=(const index& rhs) GSL_NOEXCEPT = default; 118 119 // Preconditions: component_idx < rank 120 constexpr reference operator[](std::size_t component_idx) 121 { 122 Expects(component_idx < Rank); // Component index must be less than rank 123 return elems[component_idx]; 124 } 125 126 // Preconditions: component_idx < rank 127 constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT 128 { 129 Expects(component_idx < Rank); // Component index must be less than rank 130 return elems[component_idx]; 131 } 132 133 constexpr bool operator==(const index& rhs) const GSL_NOEXCEPT 134 { 135 return std::equal(elems, elems + rank, rhs.elems); 136 } 137 138 constexpr bool operator!=(const index& rhs) const GSL_NOEXCEPT { return !(this == rhs); } 139 140 constexpr index operator+() const GSL_NOEXCEPT { return *this; } 141 142 constexpr index operator-() const GSL_NOEXCEPT 143 { 144 index ret = *this; 145 std::transform(ret, ret + rank, ret, std::negate<value_type>{}); 146 return ret; 147 } 148 149 constexpr index operator+(const index& rhs) const GSL_NOEXCEPT 150 { 151 index ret = *this; 152 ret += rhs; 153 return ret; 154 } 155 156 constexpr index operator-(const index& rhs) const GSL_NOEXCEPT 157 { 158 index ret = *this; 159 ret -= rhs; 160 return ret; 161 } 162 163 constexpr index& operator+=(const index& rhs) GSL_NOEXCEPT 164 { 165 std::transform(elems, elems + rank, rhs.elems, elems, std::plus<value_type>{}); 166 return *this; 167 } 168 169 constexpr index& operator-=(const index& rhs) GSL_NOEXCEPT 170 { 171 std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{}); 172 return *this; 173 } 174 175 constexpr index operator*(value_type v) const GSL_NOEXCEPT 176 { 177 index ret = *this; 178 ret *= v; 179 return ret; 180 } 181 182 constexpr index operator/(value_type v) const GSL_NOEXCEPT 183 { 184 index ret = *this; 185 ret /= v; 186 return ret; 187 } 188 189 friend constexpr index operator*(value_type v, const index& rhs) GSL_NOEXCEPT 190 { 191 return rhs * v; 192 } 193 194 constexpr index& operator*=(value_type v) GSL_NOEXCEPT 195 { 196 std::transform(elems, elems + rank, elems, 197 [v](value_type x) { return std::multiplies<value_type>{}(x, v); }); 198 return *this; 199 } 200 201 constexpr index& operator/=(value_type v) GSL_NOEXCEPT 202 { 203 std::transform(elems, elems + rank, elems, 204 [v](value_type x) { return std::divides<value_type>{}(x, v); }); 205 return *this; 206 } 207 208private: 209 value_type elems[Rank] = {}; 210}; 211 212#if !defined(_MSC_VER) || _MSC_VER >= 1910 213 214struct static_bounds_dynamic_range_t 215{ 216 template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>> 217 constexpr operator T() const GSL_NOEXCEPT 218 { 219 return narrow_cast<T>(-1); 220 } 221}; 222 223constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT 224{ 225 return true; 226} 227 228constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT 229{ 230 return false; 231} 232 233template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>> 234constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT 235{ 236 return narrow_cast<T>(-1) == other; 237} 238 239template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>> 240constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT 241{ 242 return right == left; 243} 244 245template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>> 246constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT 247{ 248 return narrow_cast<T>(-1) != other; 249} 250 251template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>> 252constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT 253{ 254 return right != left; 255} 256 257constexpr static_bounds_dynamic_range_t dynamic_range{}; 258#else 259const std::ptrdiff_t dynamic_range = -1; 260#endif 261 262struct generalized_mapping_tag 263{ 264}; 265struct contiguous_mapping_tag : generalized_mapping_tag 266{ 267}; 268 269namespace details 270{ 271 272 template <std::ptrdiff_t Left, std::ptrdiff_t Right> 273 struct LessThan 274 { 275 static const bool value = Left < Right; 276 }; 277 278 template <std::ptrdiff_t... Ranges> 279 struct BoundsRanges 280 { 281 using size_type = std::ptrdiff_t; 282 static const size_type Depth = 0; 283 static const size_type DynamicNum = 0; 284 static const size_type CurrentRange = 1; 285 static const size_type TotalSize = 1; 286 287 // TODO : following signature is for work around VS bug 288 template <typename OtherRange> 289 BoundsRanges(const OtherRange&, bool /* firstLevel */) 290 { 291 } 292 293 BoundsRanges(const std::ptrdiff_t* const) {} 294 BoundsRanges() = default; 295 296 template <typename T, std::size_t Dim> 297 void serialize(T&) const 298 { 299 } 300 301 template <typename T, std::size_t Dim> 302 size_type linearize(const T&) const 303 { 304 return 0; 305 } 306 307 template <typename T, std::size_t Dim> 308 size_type contains(const T&) const 309 { 310 return -1; 311 } 312 313 size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; } 314 315 size_type totalSize() const GSL_NOEXCEPT { return TotalSize; } 316 317 bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; } 318 }; 319 320 template <std::ptrdiff_t... RestRanges> 321 struct BoundsRanges<dynamic_range, RestRanges...> : BoundsRanges<RestRanges...> 322 { 323 using Base = BoundsRanges<RestRanges...>; 324 using size_type = std::ptrdiff_t; 325 static const std::size_t Depth = Base::Depth + 1; 326 static const std::size_t DynamicNum = Base::DynamicNum + 1; 327 static const size_type CurrentRange = dynamic_range; 328 static const size_type TotalSize = dynamic_range; 329 330 private: 331 size_type m_bound; 332 333 public: 334 BoundsRanges(const std::ptrdiff_t* const arr) 335 : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) 336 { 337 Expects(0 <= *arr); 338 } 339 340 BoundsRanges() : m_bound(0) {} 341 342 template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges> 343 BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other, 344 bool /* firstLevel */ = true) 345 : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false) 346 , m_bound(other.totalSize()) 347 { 348 } 349 350 template <typename T, std::size_t Dim = 0> 351 void serialize(T& arr) const 352 { 353 arr[Dim] = elementNum(); 354 this->Base::template serialize<T, Dim + 1>(arr); 355 } 356 357 template <typename T, std::size_t Dim = 0> 358 size_type linearize(const T& arr) const 359 { 360 const size_type index = this->Base::totalSize() * arr[Dim]; 361 Expects(index < m_bound); 362 return index + this->Base::template linearize<T, Dim + 1>(arr); 363 } 364 365 template <typename T, std::size_t Dim = 0> 366 size_type contains(const T& arr) const 367 { 368 const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr); 369 if (last == -1) return -1; 370 const ptrdiff_t cur = this->Base::totalSize() * arr[Dim]; 371 return cur < m_bound ? cur + last : -1; 372 } 373 374 size_type totalSize() const GSL_NOEXCEPT { return m_bound; } 375 376 size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); } 377 378 size_type elementNum(std::size_t dim) const GSL_NOEXCEPT 379 { 380 if (dim > 0) 381 return this->Base::elementNum(dim - 1); 382 else 383 return elementNum(); 384 } 385 386 bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT 387 { 388 return m_bound == rhs.m_bound && 389 static_cast<const Base&>(*this) == static_cast<const Base&>(rhs); 390 } 391 }; 392 393 template <std::ptrdiff_t CurRange, std::ptrdiff_t... RestRanges> 394 struct BoundsRanges<CurRange, RestRanges...> : BoundsRanges<RestRanges...> 395 { 396 using Base = BoundsRanges<RestRanges...>; 397 using size_type = std::ptrdiff_t; 398 static const std::size_t Depth = Base::Depth + 1; 399 static const std::size_t DynamicNum = Base::DynamicNum; 400 static const size_type CurrentRange = CurRange; 401 static const size_type TotalSize = 402 Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize; 403 404 BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {} 405 BoundsRanges() = default; 406 407 template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges> 408 BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other, 409 bool firstLevel = true) 410 : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false) 411 { 412 (void) firstLevel; 413 } 414 415 template <typename T, std::size_t Dim = 0> 416 void serialize(T& arr) const 417 { 418 arr[Dim] = elementNum(); 419 this->Base::template serialize<T, Dim + 1>(arr); 420 } 421 422 template <typename T, std::size_t Dim = 0> 423 size_type linearize(const T& arr) const 424 { 425 Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range 426 return this->Base::totalSize() * arr[Dim] + 427 this->Base::template linearize<T, Dim + 1>(arr); 428 } 429 430 template <typename T, std::size_t Dim = 0> 431 size_type contains(const T& arr) const 432 { 433 if (arr[Dim] >= CurrentRange) return -1; 434 const size_type last = this->Base::template contains<T, Dim + 1>(arr); 435 if (last == -1) return -1; 436 return this->Base::totalSize() * arr[Dim] + last; 437 } 438 439 size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); } 440 441 size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; } 442 443 size_type elementNum(std::size_t dim) const GSL_NOEXCEPT 444 { 445 if (dim > 0) 446 return this->Base::elementNum(dim - 1); 447 else 448 return elementNum(); 449 } 450 451 bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT 452 { 453 return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs); 454 } 455 }; 456 457 template <typename SourceType, typename TargetType> 458 struct BoundsRangeConvertible 459 : public std::integral_constant<bool, (SourceType::TotalSize >= TargetType::TotalSize || 460 TargetType::TotalSize == dynamic_range || 461 SourceType::TotalSize == dynamic_range || 462 TargetType::TotalSize == 0)> 463 { 464 }; 465 466 template <typename TypeChain> 467 struct TypeListIndexer 468 { 469 const TypeChain& obj_; 470 TypeListIndexer(const TypeChain& obj) : obj_(obj) {} 471 472 template <std::size_t N> 473 const TypeChain& getObj(std::true_type) 474 { 475 return obj_; 476 } 477 478 template <std::size_t N, typename MyChain = TypeChain, 479 typename MyBase = typename MyChain::Base> 480 auto getObj(std::false_type) 481 -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>()) 482 { 483 return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>(); 484 } 485 486 template <std::size_t N> 487 auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>())) 488 { 489 return getObj<N - 1>(std::integral_constant<bool, N == 0>()); 490 } 491 }; 492 493 template <typename TypeChain> 494 TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain& obj) 495 { 496 return TypeListIndexer<TypeChain>(obj); 497 } 498 499 template <std::size_t Rank, bool Enabled = (Rank > 1), 500 typename Ret = std::enable_if_t<Enabled, index<Rank - 1>>> 501 inline constexpr Ret shift_left(const index<Rank>& other) GSL_NOEXCEPT 502 { 503 Ret ret{}; 504 for (std::size_t i = 0; i < Rank - 1; ++i) { 505 ret[i] = other[i + 1]; 506 } 507 return ret; 508 } 509} 510 511template <typename IndexType> 512class bounds_iterator; 513 514template <std::ptrdiff_t... Ranges> 515class static_bounds 516{ 517public: 518 static_bounds(const details::BoundsRanges<Ranges...>&) {} 519}; 520 521template <std::ptrdiff_t FirstRange, std::ptrdiff_t... RestRanges> 522class static_bounds<FirstRange, RestRanges...> 523{ 524 using MyRanges = details::BoundsRanges<FirstRange, RestRanges...>; 525 526 MyRanges m_ranges; 527 constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} 528 529 template <std::ptrdiff_t... OtherRanges> 530 friend class static_bounds; 531 532public: 533 static const std::size_t rank = MyRanges::Depth; 534 static const std::size_t dynamic_rank = MyRanges::DynamicNum; 535 static const std::ptrdiff_t static_size = MyRanges::TotalSize; 536 537 using size_type = std::ptrdiff_t; 538 using index_type = index<rank>; 539 using const_index_type = std::add_const_t<index_type>; 540 using iterator = bounds_iterator<const_index_type>; 541 using const_iterator = bounds_iterator<const_index_type>; 542 using difference_type = std::ptrdiff_t; 543 using sliced_type = static_bounds<RestRanges...>; 544 using mapping_type = contiguous_mapping_tag; 545 546 constexpr static_bounds(const static_bounds&) = default; 547 548 template <typename SourceType, typename TargetType, std::size_t Rank> 549 struct BoundsRangeConvertible2; 550 551 template <std::size_t Rank, typename SourceType, typename TargetType, 552 typename Ret = BoundsRangeConvertible2<typename SourceType::Base, 553 typename TargetType::Base, Rank>> 554 static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret; 555 556 template <std::size_t Rank, typename SourceType, typename TargetType> 557 static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type; 558 559 template <typename SourceType, typename TargetType, std::size_t Rank> 560 struct BoundsRangeConvertible2 561 : decltype(helpBoundsRangeConvertible<Rank - 1>( 562 SourceType(), TargetType(), 563 std::integral_constant<bool, 564 SourceType::Depth == TargetType::Depth && 565 (SourceType::CurrentRange == TargetType::CurrentRange || 566 TargetType::CurrentRange == dynamic_range || 567 SourceType::CurrentRange == dynamic_range)>())) 568 { 569 }; 570 571 template <typename SourceType, typename TargetType> 572 struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type 573 { 574 }; 575 576 template <typename SourceType, typename TargetType, std::ptrdiff_t Rank = TargetType::Depth> 577 struct BoundsRangeConvertible 578 : decltype(helpBoundsRangeConvertible<Rank - 1>( 579 SourceType(), TargetType(), 580 std::integral_constant<bool, 581 SourceType::Depth == TargetType::Depth && 582 (!details::LessThan<SourceType::CurrentRange, 583 TargetType::CurrentRange>::value || 584 TargetType::CurrentRange == dynamic_range || 585 SourceType::CurrentRange == dynamic_range)>())) 586 { 587 }; 588 589 template <typename SourceType, typename TargetType> 590 struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type 591 { 592 }; 593 594 template <std::ptrdiff_t... Ranges, 595 typename = std::enable_if_t<details::BoundsRangeConvertible< 596 details::BoundsRanges<Ranges...>, 597 details::BoundsRanges<FirstRange, RestRanges...>>::value>> 598 constexpr static_bounds(const static_bounds<Ranges...>& other) : m_ranges(other.m_ranges) 599 { 600 Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges<Ranges...>::DynamicNum == 0) || 601 MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); 602 } 603 604 constexpr static_bounds(std::initializer_list<size_type> il) 605 : m_ranges(static_cast<const std::ptrdiff_t*>(il.begin())) 606 { 607 // Size of the initializer list must match the rank of the array 608 Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || 609 MyRanges::DynamicNum == il.size()); 610 // Size of the range must be less than the max element of the size type 611 Expects(m_ranges.totalSize() <= PTRDIFF_MAX); 612 } 613 614 constexpr static_bounds() = default; 615 616 constexpr sliced_type slice() const GSL_NOEXCEPT 617 { 618 return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)}; 619 } 620 621 constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; } 622 623 constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } 624 625 constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } 626 627 constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } 628 629 constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT 630 { 631 return m_ranges.contains(idx) != -1; 632 } 633 634 constexpr size_type operator[](std::size_t index) const GSL_NOEXCEPT 635 { 636 return m_ranges.elementNum(index); 637 } 638 639 template <std::size_t Dim = 0> 640 constexpr size_type extent() const GSL_NOEXCEPT 641 { 642 static_assert(Dim < rank, 643 "dimension should be less than rank (dimension count starts from 0)"); 644 return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum(); 645 } 646 647 template <typename IntType> 648 constexpr size_type extent(IntType dim) const GSL_NOEXCEPT 649 { 650 static_assert(std::is_integral<IntType>::value, 651 "Dimension parameter must be supplied as an integral type."); 652 auto real_dim = narrow_cast<std::size_t>(dim); 653 Expects(real_dim < rank); 654 655 return m_ranges.elementNum(real_dim); 656 } 657 658 constexpr index_type index_bounds() const GSL_NOEXCEPT 659 { 660 size_type extents[rank] = {}; 661 m_ranges.serialize(extents); 662 return {extents}; 663 } 664 665 template <std::ptrdiff_t... Ranges> 666 constexpr bool operator==(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT 667 { 668 return this->size() == rhs.size(); 669 } 670 671 template <std::ptrdiff_t... Ranges> 672 constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT 673 { 674 return !(*this == rhs); 675 } 676 677 constexpr const_iterator begin() const GSL_NOEXCEPT 678 { 679 return const_iterator(*this, index_type{}); 680 } 681 682 constexpr const_iterator end() const GSL_NOEXCEPT 683 { 684 return const_iterator(*this, this->index_bounds()); 685 } 686}; 687 688template <std::size_t Rank> 689class strided_bounds 690{ 691 template <std::size_t OtherRank> 692 friend class strided_bounds; 693 694public: 695 static const std::size_t rank = Rank; 696 using value_type = std::ptrdiff_t; 697 using reference = std::add_lvalue_reference_t<value_type>; 698 using const_reference = std::add_const_t<reference>; 699 using size_type = value_type; 700 using difference_type = value_type; 701 using index_type = index<rank>; 702 using const_index_type = std::add_const_t<index_type>; 703 using iterator = bounds_iterator<const_index_type>; 704 using const_iterator = bounds_iterator<const_index_type>; 705 static const value_type dynamic_rank = rank; 706 static const value_type static_size = dynamic_range; 707 using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>; 708 using mapping_type = generalized_mapping_tag; 709 710 constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default; 711 712 constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default; 713 714 constexpr strided_bounds(const value_type (&values)[rank], index_type strides) 715 : m_extents(values), m_strides(std::move(strides)) 716 { 717 } 718 719 constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT 720 : m_extents(extents), 721 m_strides(strides) 722 { 723 } 724 725 constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; } 726 727 constexpr size_type total_size() const GSL_NOEXCEPT 728 { 729 size_type ret = 0; 730 for (std::size_t i = 0; i < rank; ++i) { 731 ret += (m_extents[i] - 1) * m_strides[i]; 732 } 733 return ret + 1; 734 } 735 736 constexpr size_type size() const GSL_NOEXCEPT 737 { 738 size_type ret = 1; 739 for (std::size_t i = 0; i < rank; ++i) { 740 ret *= m_extents[i]; 741 } 742 return ret; 743 } 744 745 constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT 746 { 747 for (std::size_t i = 0; i < rank; ++i) { 748 if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; 749 } 750 return true; 751 } 752 753 constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT 754 { 755 size_type ret = 0; 756 for (std::size_t i = 0; i < rank; i++) { 757 Expects(idx[i] < m_extents[i]); // index is out of bounds of the array 758 ret += idx[i] * m_strides[i]; 759 } 760 return ret; 761 } 762 763 constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; } 764 765 template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>> 766 constexpr sliced_type slice() const 767 { 768 return {details::shift_left(m_extents), details::shift_left(m_strides)}; 769 } 770 771 template <std::size_t Dim = 0> 772 constexpr size_type extent() const GSL_NOEXCEPT 773 { 774 static_assert(Dim < Rank, 775 "dimension should be less than rank (dimension count starts from 0)"); 776 return m_extents[Dim]; 777 } 778 779 constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; } 780 constexpr const_iterator begin() const GSL_NOEXCEPT 781 { 782 return const_iterator{*this, index_type{}}; 783 } 784 785 constexpr const_iterator end() const GSL_NOEXCEPT 786 { 787 return const_iterator{*this, index_bounds()}; 788 } 789 790private: 791 index_type m_extents; 792 index_type m_strides; 793}; 794 795template <typename T> 796struct is_bounds : std::integral_constant<bool, false> 797{ 798}; 799template <std::ptrdiff_t... Ranges> 800struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true> 801{ 802}; 803template <std::size_t Rank> 804struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true> 805{ 806}; 807 808template <typename IndexType> 809class bounds_iterator : public std::iterator<std::random_access_iterator_tag, IndexType> 810{ 811private: 812 using Base = std::iterator<std::random_access_iterator_tag, IndexType>; 813 814public: 815 static const std::size_t rank = IndexType::rank; 816 using typename Base::reference; 817 using typename Base::pointer; 818 using typename Base::difference_type; 819 using typename Base::value_type; 820 using index_type = value_type; 821 using index_size_type = typename IndexType::value_type; 822 template <typename Bounds> 823 explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT 824 : boundary_(bnd.index_bounds()), 825 curr_(std::move(curr)) 826 { 827 static_assert(is_bounds<Bounds>::value, "Bounds type must be provided"); 828 } 829 830 constexpr reference operator*() const GSL_NOEXCEPT { return curr_; } 831 832 constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; } 833 834 constexpr bounds_iterator& operator++() GSL_NOEXCEPT 835 { 836 for (std::size_t i = rank; i-- > 0;) { 837 if (curr_[i] < boundary_[i] - 1) { 838 curr_[i]++; 839 return *this; 840 } 841 curr_[i] = 0; 842 } 843 // If we're here we've wrapped over - set to past-the-end. 844 curr_ = boundary_; 845 return *this; 846 } 847 848 constexpr bounds_iterator operator++(int) GSL_NOEXCEPT 849 { 850 auto ret = *this; 851 ++(*this); 852 return ret; 853 } 854 855 constexpr bounds_iterator& operator--() GSL_NOEXCEPT 856 { 857 if (!less(curr_, boundary_)) { 858 // if at the past-the-end, set to last element 859 for (std::size_t i = 0; i < rank; ++i) { 860 curr_[i] = boundary_[i] - 1; 861 } 862 return *this; 863 } 864 for (std::size_t i = rank; i-- > 0;) { 865 if (curr_[i] >= 1) { 866 curr_[i]--; 867 return *this; 868 } 869 curr_[i] = boundary_[i] - 1; 870 } 871 // If we're here the preconditions were violated 872 // "pre: there exists s such that r == ++s" 873 Expects(false); 874 return *this; 875 } 876 877 constexpr bounds_iterator operator--(int) GSL_NOEXCEPT 878 { 879 auto ret = *this; 880 --(*this); 881 return ret; 882 } 883 884 constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT 885 { 886 bounds_iterator ret{*this}; 887 return ret += n; 888 } 889 890 constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT 891 { 892 auto linear_idx = linearize(curr_) + n; 893 std::remove_const_t<value_type> stride = 0; 894 stride[rank - 1] = 1; 895 for (std::size_t i = rank - 1; i-- > 0;) { 896 stride[i] = stride[i + 1] * boundary_[i + 1]; 897 } 898 for (std::size_t i = 0; i < rank; ++i) { 899 curr_[i] = linear_idx / stride[i]; 900 linear_idx = linear_idx % stride[i]; 901 } 902 // index is out of bounds of the array 903 Expects(!less(curr_, index_type{}) && !less(boundary_, curr_)); 904 return *this; 905 } 906 907 constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT 908 { 909 bounds_iterator ret{*this}; 910 return ret -= n; 911 } 912 913 constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } 914 915 constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT 916 { 917 return linearize(curr_) - linearize(rhs.curr_); 918 } 919 920 constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } 921 922 constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT 923 { 924 return curr_ == rhs.curr_; 925 } 926 927 constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT 928 { 929 return !(*this == rhs); 930 } 931 932 constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT 933 { 934 return less(curr_, rhs.curr_); 935 } 936 937 constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT 938 { 939 return !(rhs < *this); 940 } 941 942 constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } 943 944 constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT 945 { 946 return !(rhs > *this); 947 } 948 949 void swap(bounds_iterator& rhs) GSL_NOEXCEPT 950 { 951 std::swap(boundary_, rhs.boundary_); 952 std::swap(curr_, rhs.curr_); 953 } 954 955private: 956 constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT 957 { 958 for (std::size_t i = 0; i < rank; ++i) { 959 if (one[i] < other[i]) return true; 960 } 961 return false; 962 } 963 964 constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT 965 { 966 // TODO: Smarter impl. 967 // Check if past-the-end 968 index_size_type multiplier = 1; 969 index_size_type res = 0; 970 if (!less(idx, boundary_)) { 971 res = 1; 972 for (std::size_t i = rank; i-- > 0;) { 973 res += (idx[i] - 1) * multiplier; 974 multiplier *= boundary_[i]; 975 } 976 } 977 else 978 { 979 for (std::size_t i = rank; i-- > 0;) { 980 res += idx[i] * multiplier; 981 multiplier *= boundary_[i]; 982 } 983 } 984 return res; 985 } 986 987 value_type boundary_; 988 std::remove_const_t<value_type> curr_; 989}; 990 991template <typename IndexType> 992bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n, 993 const bounds_iterator<IndexType>& rhs) GSL_NOEXCEPT 994{ 995 return rhs + n; 996} 997 998namespace details 999{ 1000 template <typename Bounds> 1001 inline constexpr std::enable_if_t< 1002 std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value, 1003 typename Bounds::index_type> 1004 make_stride(const Bounds& bnd) GSL_NOEXCEPT 1005 { 1006 return bnd.strides(); 1007 } 1008 1009 // Make a stride vector from bounds, assuming contiguous memory. 1010 template <typename Bounds> 1011 inline constexpr std::enable_if_t< 1012 std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value, 1013 typename Bounds::index_type> 1014 make_stride(const Bounds& bnd) GSL_NOEXCEPT 1015 { 1016 auto extents = bnd.index_bounds(); 1017 typename Bounds::size_type stride[Bounds::rank] = {}; 1018 1019 stride[Bounds::rank - 1] = 1; 1020 for (std::size_t i = 1; i < Bounds::rank; ++i) { 1021 stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; 1022 } 1023 return {stride}; 1024 } 1025 1026 template <typename BoundsSrc, typename BoundsDest> 1027 void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest) 1028 { 1029 static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value, 1030 "The src type and dest type must be bounds"); 1031 static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::value, 1032 "The source type must be a contiguous bounds"); 1033 static_assert(BoundsDest::static_size == dynamic_range || 1034 BoundsSrc::static_size == dynamic_range || 1035 BoundsDest::static_size == BoundsSrc::static_size, 1036 "The source bounds must have same size as dest bounds"); 1037 Expects(src.size() == dest.size()); 1038 } 1039 1040} // namespace details 1041 1042template <typename Span> 1043class contiguous_span_iterator; 1044template <typename Span> 1045class general_span_iterator; 1046 1047template <std::ptrdiff_t DimSize = dynamic_range> 1048struct dim_t 1049{ 1050 static const std::ptrdiff_t value = DimSize; 1051}; 1052template <> 1053struct dim_t<dynamic_range> 1054{ 1055 static const std::ptrdiff_t value = dynamic_range; 1056 const std::ptrdiff_t dvalue; 1057 dim_t(std::ptrdiff_t size) : dvalue(size) {} 1058}; 1059 1060template <std::ptrdiff_t N, class = std::enable_if_t<(N >= 0)>> 1061inline constexpr dim_t<N> dim() GSL_NOEXCEPT 1062{ 1063 return dim_t<N>(); 1064} 1065 1066template <std::ptrdiff_t N = dynamic_range, class = std::enable_if_t<N == dynamic_range>> 1067inline constexpr dim_t<N> dim(std::ptrdiff_t n) GSL_NOEXCEPT 1068{ 1069 return dim_t<>(n); 1070} 1071 1072template <typename ValueType, std::ptrdiff_t FirstDimension = dynamic_range, 1073 std::ptrdiff_t... RestDimensions> 1074class multi_span; 1075 1076template <typename ValueType, std::size_t Rank> 1077class strided_span; 1078 1079namespace details 1080{ 1081 template <typename T, typename = std::true_type> 1082 struct SpanTypeTraits 1083 { 1084 using value_type = T; 1085 using size_type = std::size_t; 1086 }; 1087 1088 template <typename Traits> 1089 struct SpanTypeTraits<Traits, typename std::is_reference<typename Traits::span_traits&>::type> 1090 { 1091 using value_type = typename Traits::span_traits::value_type; 1092 using size_type = typename Traits::span_traits::size_type; 1093 }; 1094 1095 template <typename T, std::ptrdiff_t... Ranks> 1096 struct SpanArrayTraits 1097 { 1098 using type = multi_span<T, Ranks...>; 1099 using value_type = T; 1100 using bounds_type = static_bounds<Ranks...>; 1101 using pointer = T*; 1102 using reference = T&; 1103 }; 1104 template <typename T, std::ptrdiff_t N, std::ptrdiff_t... Ranks> 1105 struct SpanArrayTraits<T[N], Ranks...> : SpanArrayTraits<T, Ranks..., N> 1106 { 1107 }; 1108 1109 template <typename BoundsType> 1110 BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size 1111 { 1112 Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX); 1113 return BoundsType{totalSize}; 1114 } 1115 template <typename BoundsType> 1116 BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size 1117 { 1118 Expects(BoundsType::static_size <= totalSize); 1119 return {}; 1120 } 1121 template <typename BoundsType> 1122 BoundsType newBoundsHelper(std::ptrdiff_t totalSize) 1123 { 1124 static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1"); 1125 return newBoundsHelperImpl<BoundsType>( 1126 totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>()); 1127 } 1128 1129 struct Sep 1130 { 1131 }; 1132 1133 template <typename T, typename... Args> 1134 T static_as_multi_span_helper(Sep, Args... args) 1135 { 1136 return T{narrow_cast<typename T::size_type>(args)...}; 1137 } 1138 template <typename T, typename Arg, typename... Args> 1139 std::enable_if_t< 1140 !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T> 1141 static_as_multi_span_helper(Arg, Args... args) 1142 { 1143 return static_as_multi_span_helper<T>(args...); 1144 } 1145 template <typename T, typename... Args> 1146 T static_as_multi_span_helper(dim_t<dynamic_range> val, Args... args) 1147 { 1148 return static_as_multi_span_helper<T>(args..., val.dvalue); 1149 } 1150 1151 template <typename... Dimensions> 1152 struct static_as_multi_span_static_bounds_helper 1153 { 1154 using type = static_bounds<(Dimensions::value)...>; 1155 }; 1156 1157 template <typename T> 1158 struct is_multi_span_oracle : std::false_type 1159 { 1160 }; 1161 1162 template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions> 1163 struct is_multi_span_oracle<multi_span<ValueType, FirstDimension, RestDimensions...>> 1164 : std::true_type 1165 { 1166 }; 1167 1168 template <typename ValueType, std::ptrdiff_t Rank> 1169 struct is_multi_span_oracle<strided_span<ValueType, Rank>> : std::true_type 1170 { 1171 }; 1172 1173 template <typename T> 1174 struct is_multi_span : is_multi_span_oracle<std::remove_cv_t<T>> 1175 { 1176 }; 1177} 1178 1179template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions> 1180class multi_span 1181{ 1182 // TODO do we still need this? 1183 template <typename ValueType2, std::ptrdiff_t FirstDimension2, 1184 std::ptrdiff_t... RestDimensions2> 1185 friend class multi_span; 1186 1187public: 1188 using bounds_type = static_bounds<FirstDimension, RestDimensions...>; 1189 static const std::size_t Rank = bounds_type::rank; 1190 using size_type = typename bounds_type::size_type; 1191 using index_type = typename bounds_type::index_type; 1192 using value_type = ValueType; 1193 using const_value_type = std::add_const_t<value_type>; 1194 using pointer = std::add_pointer_t<value_type>; 1195 using reference = std::add_lvalue_reference_t<value_type>; 1196 using iterator = contiguous_span_iterator<multi_span>; 1197 using const_span = multi_span<const_value_type, FirstDimension, RestDimensions...>; 1198 using const_iterator = contiguous_span_iterator<const_span>; 1199 using reverse_iterator = std::reverse_iterator<iterator>; 1200 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 1201 using sliced_type = 1202 std::conditional_t<Rank == 1, value_type, multi_span<value_type, RestDimensions...>>; 1203 1204private: 1205 pointer data_; 1206 bounds_type bounds_; 1207 1208 friend iterator; 1209 friend const_iterator; 1210 1211public: 1212 // default constructor - same as constructing from nullptr_t 1213 constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) 1214 { 1215 static_assert(bounds_type::dynamic_rank != 0 || 1216 (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), 1217 "Default construction of multi_span<T> only possible " 1218 "for dynamic or fixed, zero-length spans."); 1219 } 1220 1221 // construct from nullptr - get an empty multi_span 1222 constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) 1223 { 1224 static_assert(bounds_type::dynamic_rank != 0 || 1225 (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), 1226 "nullptr_t construction of multi_span<T> only possible " 1227 "for dynamic or fixed, zero-length spans."); 1228 } 1229 1230 // construct from nullptr with size of 0 (helps with template function calls) 1231 template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>> 1232 constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT 1233 : multi_span(nullptr, bounds_type{}) 1234 { 1235 static_assert(bounds_type::dynamic_rank != 0 || 1236 (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), 1237 "nullptr_t construction of multi_span<T> only possible " 1238 "for dynamic or fixed, zero-length spans."); 1239 Expects(size == 0); 1240 } 1241 1242 // construct from a single element 1243 constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1}) 1244 { 1245 static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || 1246 bounds_type::static_size == 1, 1247 "Construction from a single element only possible " 1248 "for dynamic or fixed spans of length 0 or 1."); 1249 } 1250 1251 // prevent constructing from temporaries for single-elements 1252 constexpr multi_span(value_type&&) = delete; 1253 1254 // construct from pointer + length 1255 constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT 1256 : multi_span(ptr, bounds_type{size}) 1257 { 1258 } 1259 1260 // construct from pointer + length - multidimensional 1261 constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data), 1262 bounds_(std::move(bounds)) 1263 { 1264 Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); 1265 } 1266 1267 // construct from begin,end pointer pair 1268 template <typename Ptr, 1269 typename = std::enable_if_t<std::is_convertible<Ptr, pointer>::value && 1270 details::LessThan<bounds_type::dynamic_rank, 2>::value>> 1271 constexpr multi_span(pointer begin, Ptr end) 1272 : multi_span(begin, 1273 details::newBoundsHelper<bounds_type>(static_cast<pointer>(end) - begin)) 1274 { 1275 Expects(begin != nullptr && end != nullptr && begin <= static_cast<pointer>(end)); 1276 } 1277 1278 // construct from n-dimensions static array 1279 template <typename T, std::size_t N, typename Helper = details::SpanArrayTraits<T, N>> 1280 constexpr multi_span(T (&arr)[N]) 1281 : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}}) 1282 { 1283 static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value, 1284 "Cannot convert from source type to target multi_span type."); 1285 static_assert(std::is_convertible<typename Helper::bounds_type, bounds_type>::value, 1286 "Cannot construct a multi_span from an array with fewer elements."); 1287 } 1288 1289 // construct from n-dimensions dynamic array (e.g. new int[m][4]) 1290 // (precedence will be lower than the 1-dimension pointer) 1291 template <typename T, typename Helper = details::SpanArrayTraits<T, dynamic_range>> 1292 constexpr multi_span(T* const& data, size_type size) 1293 : multi_span(reinterpret_cast<pointer>(data), typename Helper::bounds_type{size}) 1294 { 1295 static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value, 1296 "Cannot convert from source type to target multi_span type."); 1297 } 1298 1299 // construct from std::array 1300 template <typename T, std::size_t N> 1301 constexpr multi_span(std::array<T, N>& arr) 1302 : multi_span(arr.data(), bounds_type{static_bounds<N>{}}) 1303 { 1304 static_assert( 1305 std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value, 1306 "Cannot convert from source type to target multi_span type."); 1307 static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value, 1308 "You cannot construct a multi_span from a std::array of smaller size."); 1309 } 1310 1311 // construct from const std::array 1312 template <typename T, std::size_t N> 1313 constexpr multi_span(const std::array<T, N>& arr) 1314 : multi_span(arr.data(), bounds_type{static_bounds<N>{}}) 1315 { 1316 static_assert(std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value, 1317 "Cannot convert from source type to target multi_span type."); 1318 static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value, 1319 "You cannot construct a multi_span from a std::array of smaller size."); 1320 } 1321 1322 // prevent constructing from temporary std::array 1323 template <typename T, std::size_t N> 1324 constexpr multi_span(std::array<T, N>&& arr) = delete; 1325 1326 // construct from containers 1327 // future: could use contiguous_iterator_traits to identify only contiguous containers 1328 // type-requirements: container must have .size(), operator[] which are value_type compatible 1329 template <typename Cont, typename DataType = typename Cont::value_type, 1330 typename = std::enable_if_t< 1331 !details::is_multi_span<Cont>::value && 1332 std::is_convertible<DataType (*)[], value_type (*)[]>::value && 1333 std::is_same<std::decay_t<decltype(std::declval<Cont>().size(), 1334 *std::declval<Cont>().data())>, 1335 DataType>::value>> 1336 constexpr multi_span(Cont& cont) 1337 : multi_span(static_cast<pointer>(cont.data()), 1338 details::newBoundsHelper<bounds_type>(narrow_cast<size_type>(cont.size()))) 1339 { 1340 } 1341 1342 // prevent constructing from temporary containers 1343 template <typename Cont, typename DataType = typename Cont::value_type, 1344 typename = std::enable_if_t< 1345 !details::is_multi_span<Cont>::value && 1346 std::is_convertible<DataType (*)[], value_type (*)[]>::value && 1347 std::is_same<std::decay_t<decltype(std::declval<Cont>().size(), 1348 *std::declval<Cont>().data())>, 1349 DataType>::value>> 1350 explicit constexpr multi_span(Cont&& cont) = delete; 1351 1352 // construct from a convertible multi_span 1353 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1354 typename OtherBounds = static_bounds<OtherDimensions...>, 1355 typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value && 1356 std::is_convertible<OtherBounds, bounds_type>::value>> 1357 constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) GSL_NOEXCEPT 1358 : data_(other.data_), 1359 bounds_(other.bounds_) 1360 { 1361 } 1362 1363 // trivial copy and move 1364 constexpr multi_span(const multi_span&) = default; 1365 constexpr multi_span(multi_span&&) = default; 1366 1367 // trivial assignment 1368 constexpr multi_span& operator=(const multi_span&) = default; 1369 constexpr multi_span& operator=(multi_span&&) = default; 1370 1371 // first() - extract the first Count elements into a new multi_span 1372 template <std::ptrdiff_t Count> 1373 constexpr multi_span<ValueType, Count> first() const GSL_NOEXCEPT 1374 { 1375 static_assert(Count >= 0, "Count must be >= 0."); 1376 static_assert(bounds_type::static_size == dynamic_range || 1377 Count <= bounds_type::static_size, 1378 "Count is out of bounds."); 1379 1380 Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); 1381 return {this->data(), Count}; 1382 } 1383 1384 // first() - extract the first count elements into a new multi_span 1385 constexpr multi_span<ValueType, dynamic_range> first(size_type count) const GSL_NOEXCEPT 1386 { 1387 Expects(count >= 0 && count <= this->size()); 1388 return {this->data(), count}; 1389 } 1390 1391 // last() - extract the last Count elements into a new multi_span 1392 template <std::ptrdiff_t Count> 1393 constexpr multi_span<ValueType, Count> last() const GSL_NOEXCEPT 1394 { 1395 static_assert(Count >= 0, "Count must be >= 0."); 1396 static_assert(bounds_type::static_size == dynamic_range || 1397 Count <= bounds_type::static_size, 1398 "Count is out of bounds."); 1399 1400 Expects(bounds_type::static_size != dynamic_range || Count <= this->size()); 1401 return {this->data() + this->size() - Count, Count}; 1402 } 1403 1404 // last() - extract the last count elements into a new multi_span 1405 constexpr multi_span<ValueType, dynamic_range> last(size_type count) const GSL_NOEXCEPT 1406 { 1407 Expects(count >= 0 && count <= this->size()); 1408 return {this->data() + this->size() - count, count}; 1409 } 1410 1411 // subspan() - create a subview of Count elements starting at Offset 1412 template <std::ptrdiff_t Offset, std::ptrdiff_t Count> 1413 constexpr multi_span<ValueType, Count> subspan() const GSL_NOEXCEPT 1414 { 1415 static_assert(Count >= 0, "Count must be >= 0."); 1416 static_assert(Offset >= 0, "Offset must be >= 0."); 1417 static_assert(bounds_type::static_size == dynamic_range || 1418 ((Offset <= bounds_type::static_size) && 1419 Count <= bounds_type::static_size - Offset), 1420 "You must describe a sub-range within bounds of the multi_span."); 1421 1422 Expects(bounds_type::static_size != dynamic_range || 1423 (Offset <= this->size() && Count <= this->size() - Offset)); 1424 return {this->data() + Offset, Count}; 1425 } 1426 1427 // subspan() - create a subview of count elements starting at offset 1428 // supplying dynamic_range for count will consume all available elements from offset 1429 constexpr multi_span<ValueType, dynamic_range> 1430 subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT 1431 { 1432 Expects((offset >= 0 && offset <= this->size()) && 1433 (count == dynamic_range || (count <= this->size() - offset))); 1434 return {this->data() + offset, count == dynamic_range ? this->length() - offset : count}; 1435 } 1436 1437 // section - creates a non-contiguous, strided multi_span from a contiguous one 1438 constexpr strided_span<ValueType, Rank> section(index_type origin, 1439 index_type extents) const GSL_NOEXCEPT 1440 { 1441 size_type size = this->bounds().total_size() - this->bounds().linearize(origin); 1442 return {&this->operator[](origin), size, 1443 strided_bounds<Rank>{extents, details::make_stride(bounds())}}; 1444 } 1445 1446 // length of the multi_span in elements 1447 constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } 1448 1449 // length of the multi_span in elements 1450 constexpr size_type length() const GSL_NOEXCEPT { return this->size(); } 1451 1452 // length of the multi_span in bytes 1453 constexpr size_type size_bytes() const GSL_NOEXCEPT 1454 { 1455 return narrow_cast<size_type>(sizeof(value_type)) * this->size(); 1456 } 1457 1458 // length of the multi_span in bytes 1459 constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); } 1460 1461 constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; } 1462 1463 static constexpr std::size_t rank() { return Rank; } 1464 1465 template <std::size_t Dim = 0> 1466 constexpr size_type extent() const GSL_NOEXCEPT 1467 { 1468 static_assert(Dim < Rank, 1469 "Dimension should be less than rank (dimension count starts from 0)."); 1470 return bounds_.template extent<Dim>(); 1471 } 1472 1473 template <typename IntType> 1474 constexpr size_type extent(IntType dim) const GSL_NOEXCEPT 1475 { 1476 return bounds_.extent(dim); 1477 } 1478 1479 constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } 1480 1481 constexpr pointer data() const GSL_NOEXCEPT { return data_; } 1482 1483 template <typename FirstIndex> 1484 constexpr reference operator()(FirstIndex index) 1485 { 1486 return this->operator[](narrow_cast<std::ptrdiff_t>(index)); 1487 } 1488 1489 template <typename FirstIndex, typename... OtherIndices> 1490 constexpr reference operator()(FirstIndex index, OtherIndices... indices) 1491 { 1492 index_type idx = {narrow_cast<std::ptrdiff_t>(index), 1493 narrow_cast<std::ptrdiff_t>(indices)...}; 1494 return this->operator[](idx); 1495 } 1496 1497 constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT 1498 { 1499 return data_[bounds_.linearize(idx)]; 1500 } 1501 1502 template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>> 1503 constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT 1504 { 1505 Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array 1506 const size_type ridx = idx * bounds_.stride(); 1507 1508 // index is out of bounds of the underlying data 1509 Expects(ridx < bounds_.total_size()); 1510 return Ret{data_ + ridx, bounds_.slice()}; 1511 } 1512 1513 constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; } 1514 1515 constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; } 1516 1517 constexpr const_iterator cbegin() const GSL_NOEXCEPT 1518 { 1519 return const_iterator{reinterpret_cast<const const_span*>(this), true}; 1520 } 1521 1522 constexpr const_iterator cend() const GSL_NOEXCEPT 1523 { 1524 return const_iterator{reinterpret_cast<const const_span*>(this), false}; 1525 } 1526 1527 constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; } 1528 1529 constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; } 1530 1531 constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT 1532 { 1533 return const_reverse_iterator{cend()}; 1534 } 1535 1536 constexpr const_reverse_iterator crend() const GSL_NOEXCEPT 1537 { 1538 return const_reverse_iterator{cbegin()}; 1539 } 1540 1541 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1542 typename Dummy = std::enable_if_t<std::is_same< 1543 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1544 constexpr bool 1545 operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1546 { 1547 return bounds_.size() == other.bounds_.size() && 1548 (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); 1549 } 1550 1551 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1552 typename Dummy = std::enable_if_t<std::is_same< 1553 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1554 constexpr bool 1555 operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1556 { 1557 return !(*this == other); 1558 } 1559 1560 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1561 typename Dummy = std::enable_if_t<std::is_same< 1562 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1563 constexpr bool 1564 operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1565 { 1566 return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); 1567 } 1568 1569 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1570 typename Dummy = std::enable_if_t<std::is_same< 1571 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1572 constexpr bool 1573 operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1574 { 1575 return !(other < *this); 1576 } 1577 1578 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1579 typename Dummy = std::enable_if_t<std::is_same< 1580 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1581 constexpr bool 1582 operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1583 { 1584 return (other < *this); 1585 } 1586 1587 template <typename OtherValueType, std::ptrdiff_t... OtherDimensions, 1588 typename Dummy = std::enable_if_t<std::is_same< 1589 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1590 constexpr bool 1591 operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT 1592 { 1593 return !(*this < other); 1594 } 1595}; 1596 1597// 1598// Free functions for manipulating spans 1599// 1600 1601// reshape a multi_span into a different dimensionality 1602// DimCount and Enabled here are workarounds for a bug in MSVC 2015 1603template <typename SpanType, typename... Dimensions2, std::size_t DimCount = sizeof...(Dimensions2), 1604 bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>> 1605inline constexpr auto as_multi_span(SpanType s, Dimensions2... dims) 1606 -> multi_span<typename SpanType::value_type, Dimensions2::value...> 1607{ 1608 static_assert(details::is_multi_span<SpanType>::value, 1609 "Variadic as_multi_span() is for reshaping existing spans."); 1610 using BoundsType = 1611 typename multi_span<typename SpanType::value_type, (Dimensions2::value)...>::bounds_type; 1612 auto tobounds = details::static_as_multi_span_helper<BoundsType>(dims..., details::Sep{}); 1613 details::verifyBoundsReshape(s.bounds(), tobounds); 1614 return {s.data(), tobounds}; 1615} 1616 1617// convert a multi_span<T> to a multi_span<const byte> 1618template <typename U, std::ptrdiff_t... Dimensions> 1619multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT 1620{ 1621 static_assert(std::is_trivial<std::decay_t<U>>::value, 1622 "The value_type of multi_span must be a trivial type."); 1623 return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()}; 1624} 1625 1626// convert a multi_span<T> to a multi_span<byte> (a writeable byte multi_span) 1627// this is not currently a portable function that can be relied upon to work 1628// on all implementations. It should be considered an experimental extension 1629// to the standard GSL interface. 1630template <typename U, std::ptrdiff_t... Dimensions> 1631multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT 1632{ 1633 static_assert(std::is_trivial<std::decay_t<U>>::value, 1634 "The value_type of multi_span must be a trivial type."); 1635 return {reinterpret_cast<byte*>(s.data()), s.size_bytes()}; 1636} 1637 1638// convert a multi_span<const byte> to a multi_span<const T> 1639// this is not currently a portable function that can be relied upon to work 1640// on all implementations. It should be considered an experimental extension 1641// to the standard GSL interface. 1642template <typename U, std::ptrdiff_t... Dimensions> 1643inline constexpr auto 1644as_multi_span(multi_span<const byte, Dimensions...> s) GSL_NOEXCEPT -> multi_span< 1645 const U, static_cast<std::ptrdiff_t>( 1646 multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range 1647 ? (static_cast<std::size_t>( 1648 multi_span<const byte, Dimensions...>::bounds_type::static_size) / 1649 sizeof(U)) 1650 : dynamic_range)> 1651{ 1652 using ConstByteSpan = multi_span<const byte, Dimensions...>; 1653 static_assert( 1654 std::is_trivial<std::decay_t<U>>::value && 1655 (ConstByteSpan::bounds_type::static_size == dynamic_range || 1656 ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0), 1657 "Target type must be a trivial type and its size must match the byte array size"); 1658 1659 Expects((s.size_bytes() % narrow_cast<std::ptrdiff_t>(sizeof(U))) == 0 && 1660 (s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))) < PTRDIFF_MAX); 1661 return {reinterpret_cast<const U*>(s.data()), 1662 s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))}; 1663} 1664 1665// convert a multi_span<byte> to a multi_span<T> 1666// this is not currently a portable function that can be relied upon to work 1667// on all implementations. It should be considered an experimental extension 1668// to the standard GSL interface. 1669template <typename U, std::ptrdiff_t... Dimensions> 1670inline constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) GSL_NOEXCEPT 1671 -> multi_span<U, narrow_cast<std::ptrdiff_t>( 1672 multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range 1673 ? static_cast<std::size_t>( 1674 multi_span<byte, Dimensions...>::bounds_type::static_size) / 1675 sizeof(U) 1676 : dynamic_range)> 1677{ 1678 using ByteSpan = multi_span<byte, Dimensions...>; 1679 static_assert( 1680 std::is_trivial<std::decay_t<U>>::value && 1681 (ByteSpan::bounds_type::static_size == dynamic_range || 1682 ByteSpan::bounds_type::static_size % static_cast<std::size_t>(sizeof(U)) == 0), 1683 "Target type must be a trivial type and its size must match the byte array size"); 1684 1685 Expects((s.size_bytes() % sizeof(U)) == 0); 1686 return {reinterpret_cast<U*>(s.data()), 1687 s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))}; 1688} 1689 1690template <typename T, std::ptrdiff_t... Dimensions> 1691inline constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args) 1692 -> multi_span<std::remove_all_extents_t<T>, Dimensions...> 1693{ 1694 return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr), 1695 details::static_as_multi_span_helper<static_bounds<Dimensions...>>(args..., 1696 details::Sep{})}; 1697} 1698 1699template <typename T> 1700inline constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) -> 1701 typename details::SpanArrayTraits<T, dynamic_range>::type 1702{ 1703 return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len}; 1704} 1705 1706template <typename T, std::size_t N> 1707inline constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type 1708{ 1709 return {arr}; 1710} 1711 1712template <typename T, std::size_t N> 1713inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr) 1714{ 1715 return {arr}; 1716} 1717 1718template <typename T, std::size_t N> 1719inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete; 1720 1721template <typename T, std::size_t N> 1722inline constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr) 1723{ 1724 return {arr}; 1725} 1726 1727template <typename T> 1728inline constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end) 1729{ 1730 return {begin, end}; 1731} 1732 1733template <typename Cont> 1734inline constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t< 1735 !details::is_multi_span<std::decay_t<Cont>>::value, 1736 multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> 1737{ 1738 Expects(arr.size() < PTRDIFF_MAX); 1739 return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())}; 1740} 1741 1742template <typename Cont> 1743inline constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< 1744 !details::is_multi_span<std::decay_t<Cont>>::value, 1745 multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete; 1746 1747// from basic_string which doesn't have nonconst .data() member like other contiguous containers 1748template <typename CharT, typename Traits, typename Allocator> 1749inline constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str) 1750 -> multi_span<CharT, dynamic_range> 1751{ 1752 Expects(str.size() < PTRDIFF_MAX); 1753 return {&str[0], narrow_cast<std::ptrdiff_t>(str.size())}; 1754} 1755 1756// strided_span is an extension that is not strictly part of the GSL at this time. 1757// It is kept here while the multidimensional interface is still being defined. 1758template <typename ValueType, std::size_t Rank> 1759class strided_span 1760{ 1761public: 1762 using bounds_type = strided_bounds<Rank>; 1763 using size_type = typename bounds_type::size_type; 1764 using index_type = typename bounds_type::index_type; 1765 using value_type = ValueType; 1766 using const_value_type = std::add_const_t<value_type>; 1767 using pointer = std::add_pointer_t<value_type>; 1768 using reference = std::add_lvalue_reference_t<value_type>; 1769 using iterator = general_span_iterator<strided_span>; 1770 using const_strided_span = strided_span<const_value_type, Rank>; 1771 using const_iterator = general_span_iterator<const_strided_span>; 1772 using reverse_iterator = std::reverse_iterator<iterator>; 1773 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 1774 using sliced_type = 1775 std::conditional_t<Rank == 1, value_type, strided_span<value_type, Rank - 1>>; 1776 1777private: 1778 pointer data_; 1779 bounds_type bounds_; 1780 1781 friend iterator; 1782 friend const_iterator; 1783 template <typename OtherValueType, std::size_t OtherRank> 1784 friend class strided_span; 1785 1786public: 1787 // from raw data 1788 constexpr strided_span(pointer ptr, size_type size, bounds_type bounds) 1789 : data_(ptr), bounds_(std::move(bounds)) 1790 { 1791 Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); 1792 // Bounds cross data boundaries 1793 Expects(this->bounds().total_size() <= size); 1794 (void) size; 1795 } 1796 1797 // from static array of size N 1798 template <size_type N> 1799 constexpr strided_span(value_type (&values)[N], bounds_type bounds) 1800 : strided_span(values, N, std::move(bounds)) 1801 { 1802 } 1803 1804 // from array view 1805 template <typename OtherValueType, std::ptrdiff_t... Dimensions, 1806 bool Enabled1 = (sizeof...(Dimensions) == Rank), 1807 bool Enabled2 = std::is_convertible<OtherValueType*, ValueType*>::value, 1808 typename Dummy = std::enable_if_t<Enabled1 && Enabled2>> 1809 constexpr strided_span(multi_span<OtherValueType, Dimensions...> av, bounds_type bounds) 1810 : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) 1811 { 1812 } 1813 1814 // convertible 1815 template <typename OtherValueType, typename Dummy = std::enable_if_t<std::is_convertible< 1816 OtherValueType (*)[], value_type (*)[]>::value>> 1817 constexpr strided_span(const strided_span<OtherValueType, Rank>& other) 1818 : data_(other.data_), bounds_(other.bounds_) 1819 { 1820 } 1821 1822 // convert from bytes 1823 template <typename OtherValueType> 1824 constexpr strided_span< 1825 typename std::enable_if<std::is_same<value_type, const byte>::value, OtherValueType>::type, 1826 Rank> 1827 as_strided_span() const 1828 { 1829 static_assert((sizeof(OtherValueType) >= sizeof(value_type)) && 1830 (sizeof(OtherValueType) % sizeof(value_type) == 0), 1831 "OtherValueType should have a size to contain a multiple of ValueTypes"); 1832 auto d = narrow_cast<size_type>(sizeof(OtherValueType) / sizeof(value_type)); 1833 1834 size_type size = this->bounds().total_size() / d; 1835 return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())), 1836 size, 1837 bounds_type{resize_extent(this->bounds().index_bounds(), d), 1838 resize_stride(this->bounds().strides(), d)}}; 1839 } 1840 1841 constexpr strided_span section(index_type origin, index_type extents) const 1842 { 1843 size_type size = this->bounds().total_size() - this->bounds().linearize(origin); 1844 return {&this->operator[](origin), size, 1845 bounds_type{extents, details::make_stride(bounds())}}; 1846 } 1847 1848 constexpr reference operator[](const index_type& idx) const 1849 { 1850 return data_[bounds_.linearize(idx)]; 1851 } 1852 1853 template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>> 1854 constexpr Ret operator[](size_type idx) const 1855 { 1856 Expects(idx < bounds_.size()); // index is out of bounds of the array 1857 const size_type ridx = idx * bounds_.stride(); 1858 1859 // index is out of bounds of the underlying data 1860 Expects(ridx < bounds_.total_size()); 1861 return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; 1862 } 1863 1864 constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } 1865 1866 template <std::size_t Dim = 0> 1867 constexpr size_type extent() const GSL_NOEXCEPT 1868 { 1869 static_assert(Dim < Rank, 1870 "dimension should be less than Rank (dimension count starts from 0)"); 1871 return bounds_.template extent<Dim>(); 1872 } 1873 1874 constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } 1875 1876 constexpr pointer data() const GSL_NOEXCEPT { return data_; } 1877 1878 constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; } 1879 1880 constexpr iterator begin() const { return iterator{this, true}; } 1881 1882 constexpr iterator end() const { return iterator{this, false}; } 1883 1884 constexpr const_iterator cbegin() const 1885 { 1886 return const_iterator{reinterpret_cast<const const_strided_span*>(this), true}; 1887 } 1888 1889 constexpr const_iterator cend() const 1890 { 1891 return const_iterator{reinterpret_cast<const const_strided_span*>(this), false}; 1892 } 1893 1894 constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; } 1895 1896 constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; } 1897 1898 constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; } 1899 1900 constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } 1901 1902 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1903 typename Dummy = std::enable_if_t<std::is_same< 1904 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1905 constexpr bool 1906 operator==(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1907 { 1908 return bounds_.size() == other.bounds_.size() && 1909 (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); 1910 } 1911 1912 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1913 typename Dummy = std::enable_if_t<std::is_same< 1914 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1915 constexpr bool 1916 operator!=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1917 { 1918 return !(*this == other); 1919 } 1920 1921 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1922 typename Dummy = std::enable_if_t<std::is_same< 1923 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1924 constexpr bool 1925 operator<(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1926 { 1927 return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); 1928 } 1929 1930 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1931 typename Dummy = std::enable_if_t<std::is_same< 1932 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1933 constexpr bool 1934 operator<=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1935 { 1936 return !(other < *this); 1937 } 1938 1939 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1940 typename Dummy = std::enable_if_t<std::is_same< 1941 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1942 constexpr bool 1943 operator>(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1944 { 1945 return (other < *this); 1946 } 1947 1948 template <typename OtherValueType, std::ptrdiff_t OtherRank, 1949 typename Dummy = std::enable_if_t<std::is_same< 1950 std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>> 1951 constexpr bool 1952 operator>=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT 1953 { 1954 return !(*this < other); 1955 } 1956 1957private: 1958 static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) 1959 { 1960 // The last dimension of the array needs to contain a multiple of new type elements 1961 Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); 1962 1963 index_type ret = extent; 1964 ret[Rank - 1] /= d; 1965 1966 return ret; 1967 } 1968 1969 template <bool Enabled = (Rank == 1), typename Dummy = std::enable_if_t<Enabled>> 1970 static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr) 1971 { 1972 // Only strided arrays with regular strides can be resized 1973 Expects(strides[Rank - 1] == 1); 1974 1975 return strides; 1976 } 1977 1978 template <bool Enabled = (Rank > 1), typename Dummy = std::enable_if_t<Enabled>> 1979 static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) 1980 { 1981 // Only strided arrays with regular strides can be resized 1982 Expects(strides[Rank - 1] == 1); 1983 // The strides must have contiguous chunks of 1984 // memory that can contain a multiple of new type elements 1985 Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); 1986 1987 for (std::size_t i = Rank - 1; i > 0; --i) { 1988 // Only strided arrays with regular strides can be resized 1989 Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); 1990 } 1991 1992 index_type ret = strides / d; 1993 ret[Rank - 1] = 1; 1994 1995 return ret; 1996 } 1997}; 1998 1999template <class Span> 2000class contiguous_span_iterator 2001 : public std::iterator<std::random_access_iterator_tag, typename Span::value_type> 2002{ 2003 using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>; 2004 2005public: 2006 using typename Base::reference; 2007 using typename Base::pointer; 2008 using typename Base::difference_type; 2009 2010private: 2011 template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions> 2012 friend class multi_span; 2013 2014 pointer data_; 2015 const Span* m_validator; 2016 void validateThis() const 2017 { 2018 // iterator is out of range of the array 2019 Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); 2020 } 2021 contiguous_span_iterator(const Span* container, bool isbegin) 2022 : data_(isbegin ? container->data_ : container->data_ + container->size()) 2023 , m_validator(container) 2024 { 2025 } 2026 2027public: 2028 reference operator*() const GSL_NOEXCEPT 2029 { 2030 validateThis(); 2031 return *data_; 2032 } 2033 pointer operator->() const GSL_NOEXCEPT 2034 { 2035 validateThis(); 2036 return data_; 2037 } 2038 contiguous_span_iterator& operator++() GSL_NOEXCEPT 2039 { 2040 ++data_; 2041 return *this; 2042 } 2043 contiguous_span_iterator operator++(int) GSL_NOEXCEPT 2044 { 2045 auto ret = *this; 2046 ++(*this); 2047 return ret; 2048 } 2049 contiguous_span_iterator& operator--() GSL_NOEXCEPT 2050 { 2051 --data_; 2052 return *this; 2053 } 2054 contiguous_span_iterator operator--(int) GSL_NOEXCEPT 2055 { 2056 auto ret = *this; 2057 --(*this); 2058 return ret; 2059 } 2060 contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT 2061 { 2062 contiguous_span_iterator ret{*this}; 2063 return ret += n; 2064 } 2065 contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT 2066 { 2067 data_ += n; 2068 return *this; 2069 } 2070 contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT 2071 { 2072 contiguous_span_iterator ret{*this}; 2073 return ret -= n; 2074 } 2075 contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } 2076 difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2077 { 2078 Expects(m_validator == rhs.m_validator); 2079 return data_ - rhs.data_; 2080 } 2081 reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } 2082 bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2083 { 2084 Expects(m_validator == rhs.m_validator); 2085 return data_ == rhs.data_; 2086 } 2087 bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2088 { 2089 return !(*this == rhs); 2090 } 2091 bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2092 { 2093 Expects(m_validator == rhs.m_validator); 2094 return data_ < rhs.data_; 2095 } 2096 bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2097 { 2098 return !(rhs < *this); 2099 } 2100 bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } 2101 bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT 2102 { 2103 return !(rhs > *this); 2104 } 2105 void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT 2106 { 2107 std::swap(data_, rhs.data_); 2108 std::swap(m_validator, rhs.m_validator); 2109 } 2110}; 2111 2112template <typename Span> 2113contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n, 2114 const contiguous_span_iterator<Span>& rhs) GSL_NOEXCEPT 2115{ 2116 return rhs + n; 2117} 2118 2119template <typename Span> 2120class general_span_iterator 2121 : public std::iterator<std::random_access_iterator_tag, typename Span::value_type> 2122{ 2123 using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>; 2124 2125public: 2126 using typename Base::reference; 2127 using typename Base::pointer; 2128 using typename Base::difference_type; 2129 using typename Base::value_type; 2130 2131private: 2132 template <typename ValueType, std::size_t Rank> 2133 friend class strided_span; 2134 2135 const Span* m_container; 2136 typename Span::bounds_type::iterator m_itr; 2137 general_span_iterator(const Span* container, bool isbegin) 2138 : m_container(container) 2139 , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) 2140 { 2141 } 2142 2143public: 2144 reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; } 2145 pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; } 2146 general_span_iterator& operator++() GSL_NOEXCEPT 2147 { 2148 ++m_itr; 2149 return *this; 2150 } 2151 general_span_iterator operator++(int) GSL_NOEXCEPT 2152 { 2153 auto ret = *this; 2154 ++(*this); 2155 return ret; 2156 } 2157 general_span_iterator& operator--() GSL_NOEXCEPT 2158 { 2159 --m_itr; 2160 return *this; 2161 } 2162 general_span_iterator operator--(int) GSL_NOEXCEPT 2163 { 2164 auto ret = *this; 2165 --(*this); 2166 return ret; 2167 } 2168 general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT 2169 { 2170 general_span_iterator ret{*this}; 2171 return ret += n; 2172 } 2173 general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT 2174 { 2175 m_itr += n; 2176 return *this; 2177 } 2178 general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT 2179 { 2180 general_span_iterator ret{*this}; 2181 return ret -= n; 2182 } 2183 general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } 2184 difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT 2185 { 2186 Expects(m_container == rhs.m_container); 2187 return m_itr - rhs.m_itr; 2188 } 2189 value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; } 2190 2191 bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT 2192 { 2193 Expects(m_container == rhs.m_container); 2194 return m_itr == rhs.m_itr; 2195 } 2196 bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } 2197 bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT 2198 { 2199 Expects(m_container == rhs.m_container); 2200 return m_itr < rhs.m_itr; 2201 } 2202 bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); } 2203 bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } 2204 bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); } 2205 void swap(general_span_iterator& rhs) GSL_NOEXCEPT 2206 { 2207 std::swap(m_itr, rhs.m_itr); 2208 std::swap(m_container, rhs.m_container); 2209 } 2210}; 2211 2212template <typename Span> 2213general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n, 2214 const general_span_iterator<Span>& rhs) GSL_NOEXCEPT 2215{ 2216 return rhs + n; 2217} 2218 2219} // namespace gsl 2220 2221#undef GSL_NOEXCEPT 2222 2223#ifdef _MSC_VER 2224#if _MSC_VER < 1910 2225 2226#undef constexpr 2227#pragma pop_macro("constexpr") 2228#endif // _MSC_VER < 1910 2229 2230#pragma warning(pop) 2231 2232#endif // _MSC_VER 2233 2234#endif // GSL_MULTI_SPAN_H 2235