Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
  3.  * It is copyright by its individual contributors, as recorded in the
  4.  * project's Git history.  See COPYING.txt at the top level for license
  5.  * terms and a link to the Git history.
  6.  */
  7. #pragma once
  8.  
  9. #include <iterator>
  10. #include <tuple>
  11. #include <type_traits>
  12. #include "dxxsconf.h"
  13. #include "ephemeral_range.h"
  14. #include <utility>
  15.  
  16. namespace d_zip {
  17.  
  18. namespace detail {
  19.  
  20. template <typename... T>
  21. void discard_arguments(T &&...)
  22. {
  23. }
  24.  
  25. template <std::size_t... N, typename... range_iterator_type>
  26. void increment_iterator(std::tuple<range_iterator_type...> &iterator, std::index_sequence<N...>)
  27. {
  28.         /* Order of evaluation is irrelevant, so pass the results to a
  29.          * discarding function.  This permits the compiler to evaluate the
  30.          * expression elements in any order.
  31.          */
  32.         discard_arguments(++(std::get<N>(iterator))...);
  33. }
  34.  
  35. template <std::size_t... N, typename... range_iterator_type>
  36. auto dereference_iterator(const std::tuple<range_iterator_type...> &iterator, std::index_sequence<N...>)
  37. {
  38.         /* std::make_tuple is not appropriate here, because the result of
  39.          * dereferencing the iterator may be a reference, and the resulting
  40.          * tuple should store the reference, not the underlying object.
  41.          * Calling std::make_tuple would decay such references into the
  42.          * underlying type.
  43.          *
  44.          * std::tie is not appropriate here, because it captures all
  45.          * arguments as `T &`, so it fails to compile if the result of
  46.          * dereferencing the iterator is not a reference.
  47.          */
  48.         return std::tuple<
  49.                 decltype(*(std::get<N>(iterator)))...
  50.                 >(*(std::get<N>(iterator))...);
  51. }
  52.  
  53. }
  54.  
  55. }
  56.  
  57. /* This iterator terminates when the first zipped range terminates.  The
  58.  * caller is responsible for ensuring that use of the zip_iterator does
  59.  * not increment past the end of any zipped range.  This can be done by
  60.  * ensuring that the first zipped range is not longer than any other
  61.  * zipped range, or by ensuring that external logic stops the traversal
  62.  * before the zip_iterator increments past the end.
  63.  *
  64.  * There is no initialization time check that the below loop would be
  65.  * safe, since a check external to the zip_iterator could stop before
  66.  * undefined behaviour occurs.
  67.  
  68.         for (auto i = zip_range.begin(), e = zip_range.end(); i != e; ++i)
  69.         {
  70.                 if (condition())
  71.                         break;
  72.         }
  73.  
  74.  */
  75. template <typename... range_iterator_type>
  76. class zip_iterator : std::tuple<range_iterator_type...>
  77. {
  78.         using base_type = std::tuple<range_iterator_type...>;
  79. protected:
  80.         /* Prior to C++17, range-based for insisted on the same type for
  81.          * `begin` and `end`, so method `end_internal` must return a full iterator,
  82.          * even though most of it is a waste.  To save some work, values that are
  83.          * used for ignored fields are default-constructed (if possible)
  84.          * instead of copy-constructed from the begin iterator.
  85.          */
  86.         template <std::size_t I, typename T>
  87.                 static typename std::enable_if<std::is_default_constructible<T>::value, T>::type end_construct_ignored_element()
  88.                 {
  89.                         return T();
  90.                 }
  91.         template <std::size_t I, typename T>
  92.                 typename std::enable_if<!std::is_default_constructible<T>::value, T>::type end_construct_ignored_element() const
  93.                 {
  94.                         return std::get<I>(*this);
  95.                 }
  96.         template <typename E0, std::size_t... N>
  97.                 zip_iterator end_internal(const E0 &e0, std::index_sequence<0, N...>) const
  98.                 {
  99.                         return zip_iterator(e0, this->template end_construct_ignored_element<N, typename std::tuple_element<N, base_type>::type>()...);
  100.                 }
  101.         using index_type = std::make_index_sequence<sizeof...(range_iterator_type)>;
  102. public:
  103.         using base_type::base_type;
  104.         auto operator*() const
  105.         {
  106.                 return d_zip::detail::dereference_iterator(*this, index_type());
  107.         }
  108.         zip_iterator &operator++()
  109.         {
  110.                 d_zip::detail::increment_iterator(*this, index_type());
  111.                 return *this;
  112.         }
  113.         bool operator!=(const zip_iterator &i) const
  114.         {
  115.                 return std::get<0>(*this) != std::get<0>(i);
  116.         }
  117.         bool operator==(const zip_iterator &i) const
  118.         {
  119.                 return !(*this != i);
  120.         }
  121. };
  122.  
  123. template <typename range0_iterator_type, typename... rangeN_iterator_type>
  124. class zip : zip_iterator<range0_iterator_type, rangeN_iterator_type...>
  125. {
  126.         range0_iterator_type m_end;
  127. public:
  128.         using range_owns_iterated_storage = std::false_type;
  129.         using iterator = zip_iterator<range0_iterator_type, rangeN_iterator_type...>;
  130.         template <typename range0, typename... rangeN>
  131.                 zip(range0 &&r0, rangeN &&... rN) :
  132.                         iterator(std::begin(r0), std::begin(rN)...), m_end(std::end(r0))
  133.         {
  134.                 static_assert((!any_ephemeral_range<range0 &&, rangeN &&...>::value), "cannot zip storage of ephemeral ranges");
  135.         }
  136.         __attribute_warn_unused_result
  137.         iterator begin() const { return *this; }
  138.         __attribute_warn_unused_result
  139.         iterator end() const
  140.         {
  141.                 return this->end_internal(m_end, typename iterator::index_type());
  142.         }
  143. };
  144.  
  145. template <typename range0, typename... rangeN>
  146. zip(range0 &&r0, rangeN &&... rN) -> zip<decltype(std::begin(r0)), decltype(std::begin(rN))...>;
  147.