A C++ iterator adapter which wraps and hides an inner iterator and converts the iterated type

前端 未结 7 1557
盖世英雄少女心
盖世英雄少女心 2020-12-09 04:25

Having toyed with this I suspect it isn\'t remotely possible, but I thought I\'d ask the experts. I have the following C++ code:

class IInterface
{
    virtual vo         


        
相关标签:
7条回答
  • 2020-12-09 05:07

    I think you have two separate issues here:

    First, create an iterator that will return the IInterface* from your list<Container::Item>. This is easily done with boost::iterator_adaptor:

    class cont_iter
      : public boost::iterator_adaptor<
            cont_iter                       // Derived
          , std::list<Container::Item>::iterator // Base
          , IInterface*                     // Value
          , boost::forward_traversal_tag    // CategoryOrTraversal
          , IInterface*                     // Reference :)
        >
    {
     public:
        cont_iter()
          : cont_iter::iterator_adaptor_() {}
    
        explicit cont_iter(const cont_iter::iterator_adaptor_::base_type& p)
          : cont_iter::iterator_adaptor_(p) {}
    
     private:
        friend class boost::iterator_core_access;
        IInterface* dereference() { return this->base()->pObject->GetInterface(); }
    };
    

    You would create this type as inner in Container and return in from its begin() and end() methods.

    Second, you want the runtime-polymorphic MagicIterator. This is exactly what any_iterator does. the MagicIterator<IInterface*> is just any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>, and cont_iter can be just assigned to it.

    0 讨论(0)
  • 2020-12-09 05:11

    Create an abstract IteratorImplementation class:

    template<typename T>
    class IteratorImplementation
    {
        public:
            virtual ~IteratorImplementation() = 0;
    
            virtual T &operator*() = 0;
            virtual const T &operator*() const = 0;
    
            virtual Iterator<T> &operator++() = 0;
            virtual Iterator<T> &operator--() = 0;
    };
    

    And an Iterator class to wrap around it:

    template<typename T>
    class Iterator
    {
        public:
            Iterator(IteratorImplementation<T> * = 0);
            ~Iterator();
    
            T &operator*();
            const T &operator*() const;
    
            Iterator<T> &operator++();
            Iterator<T> &operator--();
    
        private:
            IteratorImplementation<T> *i;
    }
    
    Iterator::Iterator(IteratorImplementation<T> *impl) :
        i(impl)
    {
    }
    
    Iterator::~Iterator()
    {
        delete i;
    }
    
    T &Iterator::operator*()
    {
        if(!impl)
        {
            // Throw exception if you please.
            return;
        }
    
        return (*impl)();
    }
    
    // etc.
    

    (You can make IteratorImplementation a class "inside" of Iterator to keep things tidy.)

    In your Container class, return an instance of Iterator with a custom subclass of IteratorImplementation in the ctor:

    class ObjectContainer
    {
        public:
            void insert(Object *o);
            // ...
    
            Iterator<Object *> begin();
            Iterator<Object *> end();
    
        private:
            class CustomIteratorImplementation :
                public IteratorImplementation<Object *>
            {
                public:
                    // Re-implement stuff here.
            }
    };
    
    Iterator<Object *> ObjectContainer::begin()
    {
        CustomIteratorImplementation *impl = new CustomIteratorImplementation();  // Wish we had C++0x's "var" here.  ;P
    
        return Iterator<Object *>(impl);
    }
    
    0 讨论(0)
  • 2020-12-09 05:19

    It really depends on the Container, because the return values of c.Begin() and c.End() are implementation-defined.

    If a list of possible Containers is known to MagicIterator, a wrapper class could be used.

    template<typename T>
    class MagicIterator
    {
        public:
            MagicIterator(std::vector<T>::const_iterator i)
            {
                vector_const_iterator = i;
            }
    
            // Reimplement similarly for more types.
            MagicIterator(std::vector<T>::iterator i);
            MagicIterator(std::list<T>::const_iterator i);
            MagicIterator(std::list<T>::iterator i);
    
            // Reimplement operators here...
    
        private:
            std::vector<T>::const_iterator vector_const_iterator;
            std::vector<T>::iterator       vector_iterator;
            std::list<T>::const_iterator   list_const_iterator;
            std::list<T>::iterator         list_iterator;
    };
    

    The easy way would be to use a template which accepts Container's type:

    // C++0x
    template<typename T>
    class Iterator :
        public T::iterator
    {
        using T::iterator::iterator;
    };
    
    for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
    {
        // ...
    }
    

    More information here.

    0 讨论(0)
  • 2020-12-09 05:21

    A visitor may be a simpler (and therefore easier to maintain) solution.

    0 讨论(0)
  • 2020-12-09 05:26

    I see no reason why you can't implement this exactly as you've laid it out... am I missing something?

    To clarify, you'll need to put some kind of accessor methods on your Container class. They can be private and you can declare MagicIterator as a friend, if you feel that's the best way to encapsulate it, but I'd expose them directly. These accessor methods would use a normal STL iterator inside Container and perform the conversion to IInterface. Thus the iterating would actually be done with the Container's accessor methods and MagicIterator would just be a kind of proxy object to make it easier. To make it reentrant, you could have the MagicIterator pass in some kind of ID to look up the STL iterator inside Container, or you could actually have it pass in the STL iterator as a void *.

    0 讨论(0)
  • 2020-12-09 05:30

    Doesn't sound too complicated. You can define the iterator outside. You can also use typedefs. Something like this would fit i think. Note that it would be way cleaner if that MagicIterator would be not a free template, but a member of Item, typedefed in Container maybe. As it's now, there is a cyclic reference in it, which make it necassary to write some ugly workaround code.

    namespace detail {
        template<typename T, typename U>
        struct constify;
    
        template<typename T, typename U>
        struct constify<T*, U*> {
            typedef T * type;
        };
    
        template<typename T, typename U>
        struct constify<T*, U const*> {
            typedef T const * type;
        };
    }
    
    template<typename DstType, 
             typename Container,
             typename InputIterator>
    struct MagicIterator;
    
    class Container
    {
    private:
        struct Item
        {
            Object* pObject;
        };
    
        std::list<Item> m_items;
    
    public:
    
        // required by every Container for the iterator
        typedef std::list<Item> iterator;
        typedef std::list<Item> const_iterator;
    
        // convenience declarations
        typedef MagicIterator< IInterface*, Container, iterator > 
            item_iterator;
        typedef MagicIterator< IInterface*, Container, const_iterator > 
            const_item_iterator;
    
        item_iterator Begin();
        item_iterator End();
    };
    
    template<typename DstType, 
             typename Container = Container,
             typename InputIterator = typename Container::iterator>
    struct MagicIterator : 
        // pick either const T or T, depending on whether it's a const_iterator.
        std::iterator<std::input_iterator_tag, 
                      typename detail::constify<
                               DstType, 
                               typename InputIterator::value_type*>::type> {
        typedef std::iterator<std::input_iterator_tag, 
                     typename detail::constify<
                              DstType, 
                              typename InputIterator::value_type*>::type> base;
        MagicIterator():wrapped() { }
        explicit MagicIterator(InputIterator const& it):wrapped(it) { }
        MagicIterator(MagicIterator const& that):wrapped(that.wrapped) { }
    
        typename base::value_type operator*() {
            return (*wrapped).pObject->GetInterface();
        }
    
        MagicIterator& operator++() {
            ++wrapped;
            return *this;
        }
    
        MagicIterator operator++(int) {
            MagicIterator it(*this);
            wrapped++;
            return it;
        }
    
        bool operator==(MagicIterator const& it) const {
            return it.wrapped == wrapped;
        }
    
        bool operator!=(MagicIterator const& it) const {
            return !(*this == it);
        }
    
        InputIterator wrapped;
    };
    
    // now that the iterator adepter is defined, we can define Begin and End
    inline Container::item_iterator Container::Begin() {
        return item_iterator(m_items.begin());
    }
    
    inline Container::item_iterator Container::End() {
        return item_iterator(m_items.end());
    }
    

    Now, start using it:

    for(MagicIterator<IInterface*> it = c.Begin(); it != c.End(); ++it) {
        // ...
    }
    

    You can also use a iterator mixin provided by boost, which works like the input version of boost::function_output_iterator. It calls your iterator's operator() which then returns the appropriate value, doing what we do above in our operator* in principle. You find it in random/detail/iterator_mixin.hpp. That would probably result in fewer code. But it also requires to wrack up our neck to ensure the friend-stuff because Item is private and the iterator isn't defined inside Item. Anyway, good luck :)

    0 讨论(0)
提交回复
热议问题