https://gitlab.opengeosys.org/ogs/ogs.git
Revision c2a478f85fe136133a4b4462a48122bb0c36fd72 authored by Dmitry Yu. Naumov on 27 April 2023, 15:11:23 UTC, committed by Dmitry Yu. Naumov on 27 April 2023, 15:11:23 UTC
Iterator concepts for the covariant view

See merge request ogs/ogs!4565
2 parent s 7116a3b + 7240436
Raw File
Tip revision: c2a478f85fe136133a4b4462a48122bb0c36fd72 authored by Dmitry Yu. Naumov on 27 April 2023, 15:11:23 UTC
Merge branch 'covariant-view' into 'master'
Tip revision: c2a478f
ContainerTools.h
/**
 * \file
 * \copyright
 * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

#pragma once

#include <memory>
#include <type_traits>

namespace BaseLib
{
namespace detail
{

template <typename Element>
struct PolymorphicRandomAccessContainerViewInterface;

template <typename Element>
struct PolymorphicRandomAccessContainerViewIterator
{
    using value_type = Element;
    using difference_type = std::ptrdiff_t;

    PolymorphicRandomAccessContainerViewIterator() = default;

    PolymorphicRandomAccessContainerViewIterator(
        std::size_t n,
        PolymorphicRandomAccessContainerViewInterface<Element> const* const
            view) noexcept
        : n_{n}, view_{view}
    {
    }

    PolymorphicRandomAccessContainerViewIterator& operator++() noexcept
    {
        ++n_;
        return *this;
    }
    PolymorphicRandomAccessContainerViewIterator operator++(int) noexcept
    {
        auto copy{*this};
        operator++();
        return copy;
    }

    PolymorphicRandomAccessContainerViewIterator& operator--() noexcept
    {
        --n_;
        return *this;
    }
    PolymorphicRandomAccessContainerViewIterator operator--(int) noexcept
    {
        auto copy{*this};
        operator--();
        return copy;
    }

    PolymorphicRandomAccessContainerViewIterator& operator+=(
        difference_type const increment) noexcept
    {
        n_ += increment;
        return *this;
    }
    PolymorphicRandomAccessContainerViewIterator& operator-=(
        difference_type const decrement) noexcept
    {
        n_ -= decrement;
        return *this;
    }

    [[nodiscard]] friend PolymorphicRandomAccessContainerViewIterator operator+(
        PolymorphicRandomAccessContainerViewIterator iterator,
        difference_type const increment) noexcept
    {
        iterator.n_ += increment;
        return iterator;
    }

    [[nodiscard]] friend PolymorphicRandomAccessContainerViewIterator operator+(
        difference_type const increment,
        PolymorphicRandomAccessContainerViewIterator iterator) noexcept
    {
        iterator.n_ += increment;
        return iterator;
    }

    [[nodiscard]] difference_type operator-(
        PolymorphicRandomAccessContainerViewIterator const other) const noexcept
    {
        assert(view_ == other.view_);
        return n_ - other.n_;
    }

    [[nodiscard]] friend PolymorphicRandomAccessContainerViewIterator operator-(
        PolymorphicRandomAccessContainerViewIterator iterator,
        difference_type const decrement) noexcept
    {
        iterator.n_ -= decrement;
        return iterator;
    }

    [[nodiscard]] Element& operator*() const { return (*view_)[n_]; }
    [[nodiscard]] Element& operator[](difference_type n) const
    {
        return (*view_)[n_ + n];
    }

    auto operator<=>(
        PolymorphicRandomAccessContainerViewIterator const&) const = default;

private:
    std::size_t n_ = 0;
    PolymorphicRandomAccessContainerViewInterface<Element> const* view_ =
        nullptr;
};

template <typename Element>
struct PolymorphicRandomAccessContainerViewInterface
{
    [[nodiscard]] PolymorphicRandomAccessContainerViewIterator<Element> begin()
        const noexcept
    {
        return {0, this};
    }
    [[nodiscard]] PolymorphicRandomAccessContainerViewIterator<Element> end()
        const noexcept
    {
        return {size(), this};
    }

    [[nodiscard]] virtual std::size_t size() const noexcept = 0;

    [[nodiscard]] virtual Element& operator[](std::size_t) const = 0;

    virtual ~PolymorphicRandomAccessContainerViewInterface() = default;
};

template <typename Element, typename Container>
struct CovariantRandomAccessContainerView final
    : PolymorphicRandomAccessContainerViewInterface<Element>
{
    explicit CovariantRandomAccessContainerView(Container& container)
        : container_{container}
    {
    }

    Element& operator[](std::size_t index) const override
    {
        if constexpr (requires {
                          typename Container::value_type::element_type;
                      })
        {
            static_assert(
                std::derived_from<typename Container::value_type::element_type,
                                  Element>);

            return *container_[index];
        }
        else
        {
            static_assert(
                std::derived_from<typename Container::value_type, Element>);

            // this restriction is necessary because of the constness
            // propagation of std::vector.
            static_assert(
                (!std::is_const_v<Container>) || std::is_const_v<Element>,
                "If the element type is non-const, the container must be "
                "non-const, too.");

            return container_[index];
        }
    }

    std::size_t size() const noexcept override { return container_.size(); }

private:
    Container& container_;
};
}  // namespace detail

/**
 * Provides access to the (possibly upcasted) elements of a random access
 * container.
 *
 * Example:
 *
 * \code{.cpp}
 * std::vector<std::unique_ptr<Pet>> pets;
 * pets.push_back(std::make_unique<Budgie>());
 * pets.push_back(std::make_unique<Dog>());
 *
 * // Pet is derived from Animal
 * auto f = [](BaseLib::PolymorphicRandomAccessContainerView<Animal> const&
 *              animals_)
 * {
 *  ASSERT_EQ(2, animals_.size());
 *
 *  for (auto& animal : animals_)
 *  {
 *      animal.color = "red";
 *  }
 * };
 *
 * f(pets);  // pass the std::vector<std::unique_ptr<Pet>> to the function
 *           // taking the view
 * \endcode
 *
 * For further usage examples take a look into the unit tests.
 */
template <typename Element>
struct PolymorphicRandomAccessContainerView
{
    PolymorphicRandomAccessContainerView(
        PolymorphicRandomAccessContainerView const&) = delete;
    PolymorphicRandomAccessContainerView(
        PolymorphicRandomAccessContainerView&&) = default;

    template <typename Container>
    explicit(false)
        // cppcheck-suppress noExplicitConstructor
        PolymorphicRandomAccessContainerView(Container& container)
        : impl_{std::make_unique<
              detail::CovariantRandomAccessContainerView<Element, Container>>(
              container)}
    {
    }

    template <typename Container>
    explicit(false)
        // cppcheck-suppress noExplicitConstructor
        PolymorphicRandomAccessContainerView(Container const& container)
        : impl_{std::make_unique<
              detail::CovariantRandomAccessContainerView<Element,
                                                         Container const>>(
              container)}
    {
    }

    PolymorphicRandomAccessContainerView& operator=(
        PolymorphicRandomAccessContainerView const&) = delete;
    PolymorphicRandomAccessContainerView& operator=(
        PolymorphicRandomAccessContainerView&&) = default;

    [[nodiscard]] detail::PolymorphicRandomAccessContainerViewIterator<Element>
    begin() const noexcept
    {
        return impl_->begin();
    }
    [[nodiscard]] detail::PolymorphicRandomAccessContainerViewIterator<Element>
    end() const noexcept
    {
        return impl_->end();
    }

    [[nodiscard]] std::size_t size() const noexcept { return impl_->size(); }

    [[nodiscard]] Element& operator[](std::size_t n) const
    {
        return (*impl_)[n];
    }

private:
    std::unique_ptr<
        detail::PolymorphicRandomAccessContainerViewInterface<Element>>
        impl_;
};
}  // namespace BaseLib
back to top