Advertisement
alaestor

Header Only C++20 Constexpr Zip (working)

Jun 6th, 2022 (edited)
997
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.74 KB | None | 0 0
  1. #pragma once
  2. /*
  3.     A stand-alone zip implementation from the FGL Library
  4.  
  5.     Doxygen comments removed. Refer to documentation.
  6.     This is a merger of constexpr_assert.hpp and zip.hpp
  7.  
  8.     Documentation: https://alaestor.github.io/libFGL/group__group-utility-zip.html
  9.     Repo: https://github.com/alaestor/libFGL
  10.     My discord: Honshitsu#9218 or visit http://discord.0x04.cc
  11.  
  12.     Quick example program:
  13.     // Output: 1 1.2 hi, 2 2.2 bye, 3 3.2 lol,
  14.  
  15.     #include <iostream>
  16.     #include <vector>
  17.     #include <array>
  18.     #include <list>
  19.  
  20.     int main()
  21.     {
  22.         std::vector my_vec{ 1,2,3,4,5,6 };
  23.         std::array  my_arr{ 1.2,2.2,3.2,4.2,5.2 };
  24.         std::list  my_list{ "hi", "bye", "lol" };
  25.  
  26.         for (const auto& [a, b, c] : fgl::czip(my_vec, my_arr, my_list))
  27.             std::cout << a << " " << b << " " << c << ", ";
  28.         std::cout << std::endl;
  29.     }
  30. */
  31.  
  32. // INLINED FILE -> fgl/debug/constexpr_assert.hpp
  33.  
  34. #ifndef FGL_DEBUG_CONSTEXPR_ASSERT_HPP_INCLUDED
  35. #define FGL_DEBUG_CONSTEXPR_ASSERT_HPP_INCLUDED
  36.  
  37. #include <type_traits> // is_constant_evaluated
  38. #include <cassert>
  39.  
  40. #ifdef NDEBUG
  41.     #ifndef FGL_DEBUG_CONSTEXPR_ASSERT
  42.         #define FGL_DEBUG_CONSTEXPR_ASSERT(assertion)
  43.     #else
  44.         #error FGL_DEBUG_CONSTEXPR_ASSERT already defined
  45.     #endif // ifndef FGL_DEBUG_CONSTEXPR_ASSERT
  46. #else
  47.     #ifndef FGL_DEBUG_CONSTEXPR_ASSERT
  48.         #define FGL_DEBUG_CONSTEXPR_ASSERT(assertion) \
  49.             if (std::is_constant_evaluated())\
  50.             {\
  51.                 _Pragma("GCC diagnostic push")\
  52.                 _Pragma("GCC diagnostic ignored \"-Wterminate\"")\
  53.                 _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"")\
  54.                 if (static_cast<bool>(assertion) == false)\
  55.                     { throw; } /* ASSERTION FAILED */\
  56.                 _Pragma("GCC diagnostic pop")\
  57.             }\
  58.             else assert((assertion));
  59.     #else
  60.         #error FGL_DEBUG_CONSTEXPR_ASSERT already defined
  61.     #endif // ifndef FGL_DEBUG_CONSTEXPR_ASSERT
  62. #endif // ifdef NDEBUG else
  63.  
  64. #ifdef FGL_SHORT_MACROS
  65.     #define FGL_DEBUG_CONSTEXPR_ASSERT_SHORT_MACROS
  66. #endif // ifdef FGL_SIMPLE_MACROS
  67.  
  68. #ifdef FGL_DEBUG_CONSTEXPR_ASSERT_SHORT_MACROS
  69.     #ifndef constexpr_assert
  70.         #define constexpr_assert(assertion) \
  71.             FGL_DEBUG_CONSTEXPR_ASSERT(assertion)
  72.     #else
  73.         #error constexpr_assert already defined
  74.     #endif // constexpr_assertS
  75. #endif // ifdef FGL_DEBUG_CONSTEXPR_ASSERT_SHORT_MACRO
  76. #endif // ifndef FGL_DEBUG_CONSTEXPR_ASSERT_HPP_INCLUDED
  77.  
  78. // INLINED FILE -> fgl/utility/zip.hpp
  79.  
  80. #ifndef FGL_UTILITY_ZIP_HPP_INCLUDED
  81. #define FGL_UTILITY_ZIP_HPP_INCLUDED
  82.  
  83. #include <cstddef> // size_t, ptrdiff_t
  84. #include <functional> // ref
  85. #include <iterator>
  86. #include <tuple>
  87. #include <ranges>
  88.  
  89. namespace fgl {
  90.  
  91. using zip_sentinel_t = std::ptrdiff_t;
  92.  
  93. namespace internal {
  94. template <std::forward_iterator ... T_iters>
  95. class forward_zip_iterator final
  96. {
  97.     zip_sentinel_t m_index{ 0 };
  98.     std::tuple<T_iters...> m_iters;
  99.  
  100.     // maybe an easier way but oh well
  101.     [[nodiscard]] static constexpr auto accessType(auto& v) noexcept
  102.     { return std::ref(v); }
  103.  
  104.     [[nodiscard]] static constexpr auto accessType(auto&& v) noexcept
  105.     { return v; }
  106.  
  107.     template <std::forward_iterator T>
  108.     using get_iter_ref_t = typename std::iterator_traits<T>::reference;
  109.  
  110. public:
  111.     using value_type = std::tuple<get_iter_ref_t<T_iters>...>;
  112.     using difference_type = decltype(m_index);
  113.     using iterator_category = std::forward_iterator_tag;
  114.  
  115.     [[nodiscard]] explicit constexpr forward_zip_iterator() noexcept
  116.         = default; // Getting a redefinition error? T_iters mustn't be empty.
  117.  
  118.     [[nodiscard]] explicit constexpr forward_zip_iterator(T_iters&& ... args)
  119.     : m_iters(std::forward<T_iters>(args)...)
  120.     { static_assert(std::forward_iterator<forward_zip_iterator<T_iters...>>); }
  121.  
  122.     constexpr forward_zip_iterator& operator++() noexcept
  123.     {
  124.         ++m_index;
  125.         std::apply([](auto && ... args) noexcept { ((++args),...); }, m_iters);
  126.         return *this;
  127.     }
  128.  
  129.     [[nodiscard]] constexpr forward_zip_iterator operator++(int)
  130.     {
  131.         forward_zip_iterator tmp = *this;
  132.         ++*this;
  133.         return tmp;
  134.     }
  135.  
  136.     [[nodiscard]] [[deprecated("Expensive & internal. Use difference_type.")]]
  137.     constexpr bool operator==(const forward_zip_iterator& rhs) const
  138.     {
  139.         const auto any_iterators_match{
  140.             [&]<std::size_t ... i>(std::index_sequence<i...>) constexpr -> bool
  141.             { return ((std::get<i>(m_iters)==std::get<i>(rhs.m_iters))||...); }
  142.         };
  143.         return any_iterators_match(std::index_sequence_for<T_iters...>());
  144.     }
  145.  
  146.     [[nodiscard]] constexpr
  147.     bool operator==(const difference_type index) const noexcept
  148.     { return m_index == index; }
  149.  
  150.     [[nodiscard]] constexpr
  151.     auto operator<=>(const difference_type index) const noexcept
  152.     { return m_index <=> index; }
  153.  
  154.     [[nodiscard]] constexpr difference_type index() const noexcept
  155.     { return m_index; }
  156.  
  157.     [[nodiscard]] constexpr value_type operator*() const
  158.     noexcept( (noexcept(*T_iters{}) && ...) )
  159.     {
  160.         return [&]<std::size_t ... i>(std::index_sequence<i...>) -> value_type
  161.         {
  162.             return std::tuple(accessType(*std::get<i>(m_iters))...);
  163.         }(std::index_sequence_for<T_iters...>());
  164.     }
  165. };
  166.  
  167. static_assert(std::forward_iterator<forward_zip_iterator<char*>>);
  168. static_assert(std::forward_iterator<forward_zip_iterator<const char*>>);
  169. static_assert(
  170.     std::sentinel_for
  171.     <
  172.         forward_zip_iterator<const double*>::difference_type,
  173.         forward_zip_iterator<const double*>
  174.     >
  175. );
  176.  
  177. [[nodiscard]] constexpr inline
  178. zip_sentinel_t shortest(const std::integral auto& ... lengths)
  179. {
  180.     return std::min({static_cast<zip_sentinel_t>(lengths)...});
  181. }
  182.  
  183. }// namespace internal
  184.  
  185. template <std::ranges::forward_range ... T_ranges>
  186. constexpr auto zip(const std::integral auto length, T_ranges&& ... ranges)
  187. {
  188.     FGL_DEBUG_CONSTEXPR_ASSERT(
  189.         static_cast<zip_sentinel_t>(length)
  190.         <= internal::shortest(std::ranges::ssize(ranges)...)
  191.     );
  192.     using fgl::internal::forward_zip_iterator;
  193.     return std::ranges::subrange(
  194.         forward_zip_iterator(std::begin(std::forward<T_ranges>(ranges))...),
  195.         static_cast<zip_sentinel_t>(length)
  196.     );
  197. }
  198.  
  199. template <std::ranges::forward_range ... T_ranges>
  200. constexpr auto zip(T_ranges&& ... ranges)
  201. {
  202.     return fgl::zip(
  203.         internal::shortest(std::ranges::ssize(ranges)...),
  204.         std::forward<T_ranges>(ranges)...
  205.     );
  206. }
  207.  
  208. template <std::ranges::forward_range ... T_ranges>
  209. constexpr auto czip(const std::integral auto length, T_ranges&& ... args)
  210. {
  211.     FGL_DEBUG_CONSTEXPR_ASSERT(
  212.         static_cast<zip_sentinel_t>(length)
  213.         <= internal::shortest(std::ranges::ssize(args)...)
  214.     );
  215.     using fgl::internal::forward_zip_iterator;
  216.     return std::ranges::subrange(
  217.         forward_zip_iterator(std::cbegin(std::forward<T_ranges>(args))...),
  218.         static_cast<zip_sentinel_t>(length)
  219.     );
  220. }
  221.  
  222. template <std::ranges::forward_range ... T_ranges>
  223. constexpr auto czip(T_ranges&& ... args)
  224. {
  225.     using internal::shortest, std::ranges::ssize;
  226.     return fgl::czip(shortest(ssize(args)...), std::forward<T_ranges>(args)...);
  227. }
  228.  
  229. } // namespace fgl
  230.  
  231. #endif // FGL_UTILITY_ZIP_HPP_INCLUDED
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement