簡體   English   中英

一個C ++迭代器適配器,它包裝和隱藏內部迭代器並轉換迭代類型

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

玩弄了這個,我懷疑它不可能,但我想我會問專家。 我有以下C ++代碼:

class IInterface
{
    virtual void SomeMethod() = 0;
};

class Object
{
    IInterface* GetInterface() { ... }
};

class Container
{
private:
    struct Item
    {
        Object* pObject;
        [... other members ...]
    };
    std::list<Item> m_items;
};

我想將這些方法添加到Container:

MagicIterator<IInterface*> Begin();
    MagicIterator<IInterface*> End();

為了讓呼叫者可以寫:

Container c = [...]
for (MagicIterator<IInterface*> i = c.Begin(); i != c.End(); i++)
{
    IInterface* pItf = *i;
    [...]
}

所以基本上我想提供一個類似乎迭代某些集合(其中Begin()和End()的調用者不允許看到)的IInterface指針,但它實際上迭代了指向其他指針的指針集合對象(Container類專用),可以轉換為IInterface指針。

幾個關鍵點:

  • MagicIterator將在Container外定義。
  • Container::Item必須保持私有狀態。
  • MagicIterator必須迭代IInterface指針,盡管Container擁有一個std::list<Container::Item> Container::Item包含Object*Object可用於獲取IInterface*
  • MagicIterator必須可以重用幾個類似於Container的類,但可能在內部有不同的列表實現,每次都以不同的方式獲取不同的對象( std::vector<SomeOtherItem>mylist<YetAnotherItem> )和IInterface*
  • MagicIterator不應該包含特定於容器的代碼,盡管它可以委托給那樣做的類,只要這樣的委托不是硬編碼到MagicIterator特定容器(例如,編譯器會以某種方式自動解析)。
  • 解決方案必須在Visual C ++下編譯而不使用其他庫(例如boost),這需要其作者的許可協議。
  • 此外,迭代可能不會分配任何堆內存(因此在任何階段都沒有new()malloc() ),也沒有memcpy()

感謝您的時間,即使您只是在閱讀; 這個人真的在煩我!

更新:雖然我有一些非常有趣的答案,但還沒有一個滿足上述所有要求。 值得注意的是,棘手的領域是:i)以某種方式將MagicIterator與Container分離(默認模板參數不會削減它),以及ii)避免堆分配; 但我真的想要一個涵蓋所有上述子彈的解決方案。

我認為你有兩個不同的問題:

首先,創建一個迭代器,它將從list<Container::Item>返回IInterface* 使用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(); }
};

您可以在Container創建此類型作為inner,並從其begin()end()方法返回。

其次,您需要運行時多態MagicIterator 這正是any_iterator所做的。 MagicIterator<IInterface*>只是any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*> ,而cont_iter只能分配給它。

創建一個抽象的IteratorImplementation類:

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;
};

還有一個Iterator它的Iterator類:

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.

(你可以讓IteratorImplementation類“內部”的Iterator ,以保持整潔。)

在你的Container類,返回的實例Iterator與一個自定義子類IteratorImplementationctor

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);
}

聽起來不太復雜。 您可以在外部定義迭代器。 您也可以使用typedef。 我認為這樣的東西是合適的。 請注意,如果MagicIterator 不是免費模板,而是Item的成員,也可以在Container中輸入typedefed。 就像現在一樣,它中有一個循環引用,這使得編寫一些丑陋的變通代碼成為必要。

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());
}

現在,開始使用它:

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

您還可以使用boost提供的迭代器mixin,它的工作方式類似於boost :: function_output_iterator的輸入版本。 它調用你的迭代器的operator()然后返回適當的值,原則上我們在operator*中執行上面的operator* 你可以在random/detail/iterator_mixin.hpp找到它。 這可能會導致更少的代碼。 但它還需要破壞我們的頸部以確保朋友的東西,因為Item是私有的,並且在Item中沒有定義迭代器。 好吧,祝你好運:)

它實際上取決於Container ,因為c.Begin()c.End()的返回值是實現定義的。

如果MagicIterator知道可能的Container列表,則可以使用包裝類。

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;
};

簡單的方法是使用一個接受Container類型的模板:

// C++0x
template<typename T>
class Iterator :
    public T::iterator
{
    using T::iterator::iterator;
};

for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
{
    // ...
}

更多信息在這里。

我沒有理由為什么你不能完全實現這一點,因為你已經解決了......我錯過了什么?

為了澄清,您需要在Container類上放置某種訪問器方法。 它們可以是私有的,如果您覺得這是封裝它的最佳方式,您可以將MagicIterator聲明為朋友,但我會直接公開它們。 這些訪問器方法將使用Container內的普通STL迭代器並執行到IInterface的轉換。 因此,迭代實際上將使用Container的訪問器方法完成,而MagicIterator只是一種代理對象,以使其更容易。 為了使它可以重入,你可以讓MagicIterator傳遞某種ID來查找Container里面的STL迭代器,或者你實際上可以讓它在STL迭代器中作為void *傳遞。

訪問者可能是一個更簡單(因此更容易維護)的解決方案。

我現在找到了一個適合我原來目的的解決方案。 我仍然不喜歡它:)

解決方案涉及MagicIterator在IInterface *上進行模板化,並且使用void *到迭代器,所述迭代器的字節大小以及指向函數的指針表,這些函數在所述void *上執行標准迭代函數,例如遞增,遞減, MagicIterator假定將給定迭代器memcpy到內部緩沖區是安全的,並通過將自己的緩沖區作為void *傳遞給提供的函數來實現自己的成員,就像它是原始迭代器一樣。

然后,Container必須實現靜態迭代函數,這些函數將提供的void *轉換回std :: list :: iterator。 Container :: begin()和Container :: end()只是構造一個std :: list :: iterator,將指向它的指針和一個迭代函數表一起傳遞給MagicIterator,然后返回MagicIterator。

這有點令人厭惡,並打破了我關於“沒有memcpy()”的原始規則,並對所討論的迭代器的內部進行了假設。 但它避免了堆分配,使Collection的內部(包括Item)保持私有,使MagicIterator完全獨立於所討論的集合和IInterface *,理論上允許MagicIterators使用任何集合(假設它的迭代器可以安全地memcopy()' d)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM