Manu343726
6/16/2015 - 8:07 PM

Type-erased C++ ranges

Type-erased C++ ranges

//
// Created by manu343726 on 16/06/15.
//

#ifndef ERASED_RANGE_HPP
#define ERASED_RANGE_HPP

#include <range/v3/all.hpp>
#include <type_traits>

template<typename ValueType>
struct erased_iterator
{
    template<typename It>
    erased_iterator(It it) : it_{new PolyIterator<It>{it}}
    {}

    erased_iterator() = default;

    erased_iterator& operator++()
    {
        it_->next();

        return *this;
    }

    erased_iterator& operator--()
    {
        it_->prev();

        return *this;
    }

    erased_iterator operator++(int)
    {
        erased_iterator tmp{*this};

        ++(*this);

        return std::move(tmp);
    }

    erased_iterator operator--(int)
    {
        erased_iterator tmp{*this};

        --(*this);

        return std::move(tmp);
    }

    template<typename It>
    erased_iterator& operator=(It&& it)
    {
        if(it_ != nullptr) {
            it_->~iterator_base();
            it_ = new(it_) PolyIterator <typename std::decay<It>::type>{it};
        }
        else
            it_ = new PolyIterator<typename std::decay<It>::type>{it};

        return *this;
    }

    const ValueType& operator*() const
    {
        return it_->deref();
    }

    ValueType& operator*()
    {
        return it_->deref();
    }

    friend bool operator==(const erased_iterator& lhs, const erased_iterator& rhs)
    {
        return *(lhs.it_) == *(rhs.it_);
    }

    friend bool operator!=(const erased_iterator& lhs, const erased_iterator& rhs)
    {
        return !(lhs == rhs);
    }

    erased_iterator(const erased_iterator& other) :
        it_{other.it_->clone()}
    {}

    erased_iterator(erased_iterator&& other) :
        it_{std::move(other.it_)}
    {
        other.it_ = nullptr;
    }

    erased_iterator& operator=(const erased_iterator& other)
    {
        if(it_ != nullptr)
            it_ = other.it_->clone(it_);
        else
            it_ = other.it_->clone();

        return *this;
    }

    erased_iterator& operator=(erased_iterator&& other)
    {
        std::swap(*this, other);

        return *this;
    }

    ~erased_iterator()
    {
        delete it_;
    }
private:

    struct iterator_base
    {
        virtual iterator_base& next() = 0;
        virtual iterator_base& prev() = 0;
        virtual const ValueType& deref() const = 0;
        virtual ValueType& deref() = 0;
        virtual bool equals(const iterator_base& lhs, const iterator_base& rhs) const = 0;

        virtual iterator_base* clone() const = 0;
        virtual iterator_base* clone(iterator_base*) const = 0;

        virtual ~iterator_base() = default;

        friend bool operator==(const iterator_base& lhs, const iterator_base& rhs)
        {
            return lhs.equals(lhs, rhs);
        }

        friend bool operator !=(const iterator_base& lhs, const iterator_base& rhs)
        {
            return !(lhs == rhs);
        }
    };

    template<typename It>
    struct PolyIterator : public iterator_base
    {
        friend class erased_iterator;

        PolyIterator(It it) : it_{it}
        {}

        iterator_base& next() override
        {
            it_ = std::next(it_);

            return *this;
        }

        iterator_base& prev() override
        {
            it_ = std::prev(it_);

            return *this;
        }

        const ValueType& deref() const override
        {
            return static_cast<const ValueType&>(*it_);
        }

        ValueType& deref() override
        {
            return static_cast<ValueType&>(*it_);
        }

        bool equals(const iterator_base& lhs, const iterator_base& rhs) const override
        {
            return reinterpret_cast<const PolyIterator&>(lhs).it_ == reinterpret_cast<const PolyIterator&>(rhs).it_;
        }

        iterator_base* clone() const override
        {
            return new PolyIterator{it_};
        }

        iterator_base* clone(iterator_base* ptr) const override
        {
            ptr->~iterator_base();
            return new(ptr) PolyIterator{it_};
        }

    private:
        It it_;
    };

    iterator_base* it_ = nullptr;
};

template<typename ValueType>
struct erased_range
{
    using iterator = erased_iterator<ValueType>;
    using value_type = ValueType;

    erased_range() = default;

    template<typename Range>
    erased_range(const Range& range)
    {
        auto bounded = ranges::view::bounded(range);
        begin_ = std::begin(bounded);
        end_ = std::end(bounded);
    }

    template<typename Range>
    erased_range(Range& range)
    {
        auto bounded = ranges::view::bounded(range);
        begin_ = std::begin(bounded);
        end_ = std::end(bounded);
    }

    template<typename Range>
    erased_range& operator=(const Range& range)
    {
        auto bounded = ranges::view::bounded(range);

        begin_ = std::begin(range);
        end_ = std::end(range);

        return *this;
    }

    template<typename Range>
    erased_range& operator=(Range& range)
    {
        auto bounded = ranges::view::bounded(range);

        begin_ = std::begin(range);
        end_ = std::end(range);

        return *this;
    }

    iterator begin() const
    {
        return begin_;
    }

    iterator end() const
    {
        return end_;
    }

private:
    iterator begin_;
    iterator end_;
};

#endif //ERASED_RANGE_HPP