Program Listing for File multirator.hpp

Return to documentation for file (include/util/multirator.hpp)

// SPDX-FileCopyrightText: 2021 Christian Göhring <mostsig@gmail.com>
// SPDX-License-Identifier: MIT

#ifndef THAT_THIS_UTIL_MULTIRATOR_HEADER_IS_ALREADY_INCLUDED
#define THAT_THIS_UTIL_MULTIRATOR_HEADER_IS_ALREADY_INCLUDED

#include <array>
#include <cstddef>
#include <cstring>
#include <iterator>
#include <tuple>
#include <type_traits>

namespace util {

namespace detail {

template <class... Iterators, std::size_t... I>
auto make_reference_tuple(std::tuple<Iterators...>& iterators, std::index_sequence<I...>);

template <class... Iterators, std::size_t... I>
auto iterator_tuple_neq(const std::tuple<Iterators...>& lhs, const std::tuple<Iterators...>& rhs,
                        std::index_sequence<I...>);

template <class... Containers, std::size_t... I>
auto make_const_iterator_begin_tuple(const std::tuple<const Containers*...>& containers,
                                     std::index_sequence<I...>);

template <class... Containers, std::size_t... I>
auto make_const_iterator_end_tuple(const std::tuple<const Containers*...>& containers,
                                   std::index_sequence<I...>);

template <class... Containers, std::size_t... I>
auto make_iterator_begin_tuple(const std::tuple<Containers*...>& containers,
                               std::index_sequence<I...>);

template <class... Containers, std::size_t... I>
auto make_iterator_end_tuple(const std::tuple<Containers*...>& containers,
                             std::index_sequence<I...>);

}  // namespace detail

template <class... Iterators>
class multirator {
public:
    using value_type = std::tuple<typename Iterators::reference...>;
    using reference = std::tuple<typename Iterators::reference...>&;
    using const_reference = const std::tuple<typename Iterators::reference...>&;

    explicit multirator(Iterators... iterators) : iterators(iterators...) {}

    explicit multirator(std::tuple<Iterators...> iterator_tuple) : iterators(iterator_tuple) {}

    auto operator++() -> multirator& {
        std::apply([&](auto&... it) { ((it++), ...); }, iterators);
        return *this;
    }

    auto operator+(int i) -> multirator& {
        std::apply([&](auto&... it) { ((it += i), ...); }, iterators);
        return *this;
    }

    auto operator!=(const multirator& other) const noexcept -> bool {
        return detail::iterator_tuple_neq(iterators, other.iterators,
                                          std::index_sequence_for<Iterators...>{});
    }

    auto operator*() noexcept -> reference {
        auto tuple =
            detail::make_reference_tuple(iterators, std::index_sequence_for<Iterators...>{});
        std::memcpy(static_cast<void*>(&values[0]), static_cast<const void*>(&tuple),
                    sizeof(value_type));
        return reinterpret_cast<reference>(values);
    }

    auto operator*() const noexcept -> const_reference {
        auto tuple =
            detail::make_reference_tuple(iterators, std::index_sequence_for<Iterators...>{});
        std::memcpy(static_cast<void*>(&values[0]), static_cast<const void*>(&tuple),
                    sizeof(value_type));
        return reinterpret_cast<const_reference>(values);
    }

private:
    std::tuple<Iterators...> iterators;
    std::array<uint8_t, sizeof(value_type)> values;
};

template <class... Containers>
class const_multirator_range {
public:
    using const_iterator = multirator<typename Containers::const_iterator...>;

    explicit const_multirator_range(const Containers&... containers);

    auto begin() const -> const_iterator;
    auto end() const -> const_iterator;

private:
    std::tuple<const Containers*...> const_containers;
};

template <class... Containers>
class multirator_range {
    static constexpr bool Const = std::conjunction<std::is_const<Containers>...>::value;

public:
    using iterator = multirator<typename Containers::iterator...>;
    using const_iterator = typename const_multirator_range<Containers...>::const_iterator;

    explicit multirator_range(Containers&... containers);

    auto begin() -> iterator;
    auto end() -> iterator;
    auto cbegin() -> const_iterator;
    auto cend() -> const_iterator;

private:
    std::tuple<Containers*...> containers;
};

template <class... Containers>
const_multirator_range<Containers...>::const_multirator_range(const Containers&... containers)
    : const_containers(std::addressof(containers)...) {}

template <class... Containers>
auto const_multirator_range<Containers...>::begin() const -> const_iterator {
    return const_iterator(detail::make_const_iterator_begin_tuple(
        const_containers, std::index_sequence_for<Containers...>{}));
};

template <class... Containers>
auto const_multirator_range<Containers...>::end() const -> const_iterator {
    return const_iterator(detail::make_const_iterator_end_tuple(
        const_containers, std::index_sequence_for<Containers...>{}));
};

template <class... Containers>
multirator_range<Containers...>::multirator_range(Containers&... containers)
    : containers(std::addressof(containers)...) {}

template <class... Containers>
auto multirator_range<Containers...>::begin() -> iterator {
    return iterator(
        detail::make_iterator_begin_tuple(containers, std::index_sequence_for<Containers...>{}));
}

template <class... Containers>
auto multirator_range<Containers...>::end() -> iterator {
    return iterator(
        detail::make_iterator_end_tuple(containers, std::index_sequence_for<Containers...>{}));
}

template <class... Containers>
auto multirator_range<Containers...>::cbegin() -> const_iterator {
    return const_iterator(detail::make_const_iterator_begin_tuple(
        containers, std::index_sequence_for<Containers...>{}));
}

template <class... Containers>
auto multirator_range<Containers...>::cend() -> const_iterator {
    return const_iterator(detail::make_const_iterator_end_tuple(
        containers, std::index_sequence_for<Containers...>{}));
}

template <class... Containers>
auto make_multirator_range(Containers&... containers) {
    return multirator_range(containers...);
}

template <class... Containers>
auto make_multirator_range(const Containers&... containers) {
    return const_multirator_range(containers...);
}

template <class... Iterators, std::size_t... I>
auto detail::make_reference_tuple(std::tuple<Iterators...>& iterators, std::index_sequence<I...>) {
    return std::tuple<typename Iterators::reference...>(std::get<I>(iterators).operator*()...);
}

template <class... Iterators, std::size_t... I>
auto detail::iterator_tuple_neq(const std::tuple<Iterators...>& lhs,
                                const std::tuple<Iterators...>& rhs, std::index_sequence<I...>) {
    return ((std::get<I>(lhs) != std::get<I>(rhs)) && ...);
}

template <class... Containers, std::size_t... I>
auto detail::make_const_iterator_begin_tuple(const std::tuple<const Containers*...>& containers,
                                             std::index_sequence<I...>) {
    return std::tuple<typename Containers::const_iterator...>(begin(*std::get<I>(containers))...);
}

template <class... Containers, std::size_t... I>
auto detail::make_const_iterator_end_tuple(const std::tuple<const Containers*...>& containers,
                                           std::index_sequence<I...>) {
    return std::tuple<typename Containers::const_iterator...>(end(*std::get<I>(containers))...);
}

template <class... Containers, std::size_t... I>
auto detail::make_iterator_begin_tuple(const std::tuple<Containers*...>& containers,
                                       std::index_sequence<I...>) {
    return std::tuple<typename Containers::iterator...>(begin(*std::get<I>(containers))...);
}

template <class... Containers, std::size_t... I>
auto detail::make_iterator_end_tuple(const std::tuple<Containers*...>& containers,
                                     std::index_sequence<I...>) {
    return std::tuple<typename Containers::iterator...>(end(*std::get<I>(containers))...);
}

}  // namespace util

#endif  // THAT_THIS_UTIL_MULTIRATOR_HEADER_IS_ALREADY_INCLUDED