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