Skip to content

Instantly share code, notes, and snippets.

@ConorWilliams
Last active July 1, 2021 15:30
Show Gist options
  • Save ConorWilliams/2dc0be178c0917617e475f9da27bf3c6 to your computer and use it in GitHub Desktop.
Save ConorWilliams/2dc0be178c0917617e475f9da27bf3c6 to your computer and use it in GitHub Desktop.
Zip like Python in C++! A zip() function for ranges based for loops. See it working: https://godbolt.org/z/6AJdhk
// Copyright (c) 2019, Conor Williams
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// Compiling on gcc 9.2 with: -std=c++2a -O3 -fconcepts
#ifndef ZIP_CPP
#define ZIP_CPP
#include <tuple>
#include <utility>
#include <type_traits>
namespace zipper {
// ****************************************************************************
// * Reusable Tuple Machinery *
// ****************************************************************************
namespace detail {
template <typename>
struct is_tuple : std::false_type {};
template <typename... Args>
struct is_tuple<std::tuple<Args...>> : std::true_type {};
template <typename... Args>
std::tuple<Args...> make_universal_tuple(Args&&... args) {
return std::tuple<Args...>{std::forward<Args>(args)...};
}
} // namespace detail
template <typename T>
concept Tuple = detail::is_tuple<std::decay_t<T>>::value;
// (tup{a,b,c}, foo) -> tup{foo(a), foo(b), foo(c)}
template <Tuple T, typename F>
constexpr auto transform(T&& tup, F&& foo) {
return std::apply(
[foo = std::forward<F>(foo)](auto&&... args) {
return detail::make_universal_tuple(
foo(std::forward<decltype(args)>(args))...);
},
std::forward<T>(tup));
}
// Applies foo to every element in tuple
template <Tuple T, typename F>
constexpr void for_each(T&& tup, F&& foo) {
std::apply(
[foo = std::forward<F>(foo)](auto&&... args) {
/**
* (void) required to prevent classes that overload the comma
* operator performing extra operations.
*/
(((void)foo(std::forward<decltype(args)>(args))), ...);
},
std::forward<T>(tup));
}
// ****************************************************************************
// * Zip Class *
// ****************************************************************************
namespace detail {
template <Tuple A, Tuple B, std::size_t... I>
constexpr bool none_equal(A&& a, B&& b, std::index_sequence<I...>) {
return (... && (std::get<I>(std::forward<A>(a)) !=
std::get<I>(std::forward<B>(b))));
}
} // namespace detail
template <Tuple A, Tuple B>
concept same_size =
std::tuple_size_v<std::decay_t<A>> == std::tuple_size_v<std::decay_t<B>>;
template <Tuple A, Tuple B>
requires same_size<A, B> constexpr bool none_equal(A&& a, B&& b) {
return detail::none_equal(
std::forward<A>(a), std::forward<B>(b),
std::make_index_sequence<std::tuple_size_v<std::decay_t<A>>>{});
}
template <Tuple T>
class ZipIter {
private:
T iters;
public:
constexpr explicit ZipIter(T&& tuple) : iters{std::forward<T>(tuple)} {}
constexpr bool operator!=(ZipIter const& other) const {
return none_equal(iters, other.iters);
}
ZipIter& operator++() {
for_each(iters, [](auto&& x) { x++; });
return *this;
}
constexpr auto operator*() const {
/**
* Perfect forwarding of return type allows reference capture for
* iterators returning references to members.
*/
return transform(iters, [](auto&& x) -> decltype(*x) { return *x; });
}
};
template <typename... Args>
class Zip {
private:
std::tuple<Args...> const tup;
public:
constexpr explicit Zip(Args&&... args) : tup{std::forward<Args>(args)...} {}
constexpr auto begin() {
return ZipIter{transform(tup, [](auto&& x) { return std::begin(x); })};
}
constexpr auto end() {
return ZipIter{transform(tup, [](auto&& x) { return std::end(x); })};
}
};
} // namespace zipper
// Convenience function to make Zip object
template <typename... Args>
inline constexpr zipper::Zip<Args...> zip(Args&&... args) {
return zipper::Zip<Args...>{std::forward<Args>(args)...};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment