Адаптер итератора C++, который обертывает и скрывает внутренний итератор и преобразовывает выполненный с помощью итераций тип

Я не уверен, что было бы справедливо подразумевать, что функциональное программирование могло предложить какие-либо преимущества перед императивным программированием с точки зрения производительности при работе с огромным количеством данных.

Я думаю, вам нужно добавить еще один инструмент в ваш инструментарий, и это может быть RxJS .

RxJS - это библиотека для составления асинхронных и событийных программ с использованием наблюдаемых последовательностей.

blockquote>

Если вы не знакомы с RxJS или реактивным программированием в целом, мои примеры определенно будут выглядеть странно, но я думаю, что было бы неплохо познакомиться с этими концепциями [1114 ]

В вашем случае наблюдаемая последовательность - это ваш экземпляр MongoDB, который со временем генерирует записи.

Я собираюсь подделать вашу базу данных:

var db = range(1, 5);

Функция range - это функция RxJS, которая будет выдавать значение в указанном диапазоне.

db.subscribe(n => {
  console.log(`record ${n}`);
});

//=> record 1
//=> record 2
//=> record 3
//=> record 4
//=> record 5

Теперь меня интересуют только первая и последняя запись.

Я могу создать наблюдаемую, которая будет излучать только первую запись, и создать другую, которая будет излучать только последнюю:

var db = range(1, 5);
var firstRecord = db.pipe(first());
var lastRecord = db.pipe(last());

merge(firstRecord, lastRecord).subscribe(n => {
  console.log(`record ${n}`);
});
//=> record 1
//=> record 5

Однако мне также нужно обрабатывать все записи партиями: (в в этом примере я собираюсь создать партии по 10 записей в каждой)

var db = range(1, 100);
var batches = db.pipe(bufferCount(10))
var firstRecord = db.pipe(first());
var lastRecord = db.pipe(last());

merge(firstRecord, batches, lastRecord).subscribe(n => {
  console.log(`record ${n}`);
});

//=> record 1
//=> record 1,2,3,4,5,6,7,8,9,10
//=> record 11,12,13,14,15,16,17,18,19,20
//=> record 21,22,23,24,25,26,27,28,29,30
//=> record 31,32,33,34,35,36,37,38,39,40
//=> record 41,42,43,44,45,46,47,48,49,50
//=> record 51,52,53,54,55,56,57,58,59,60
//=> record 61,62,63,64,65,66,67,68,69,70
//=> record 71,72,73,74,75,76,77,78,79,80
//=> record 81,82,83,84,85,86,87,88,89,90
//=> record 91,92,93,94,95,96,97,98,99,100
//=> record 100

Как вы можете видеть в выводе, он выпустил:

  1. Первая запись
  2. [ 118] Десять партий по 10 записей в каждой
  3. Последняя запись

Я не буду пытаться решить ваше упражнение для вас, и я не слишком знаком с RxJS, чтобы расширять тоже много об этом.

Я просто хотел показать вам другой способ и дать вам понять, что это можно совместить с функциональным программированием.

Надеюсь, это поможет.

15
задан 8 revs, 2 users 99% 24 January 2009 в 15:02
поделиться

7 ответов

Я теперь нашел решение, которое более пригодно для моей исходной цели. Мне все еще не нравится он хотя :)

Решение вовлекает MagicIterator, шаблонный на IInterface* и создаваемый и с пустотой* к итератору, размер байта упомянутого итератора и таблица указателей на функции, которые выполняют стандартные итеративные функции на упомянутой пустоте*, такие как инкремент, декремент, разыменовывают и т.д. MagicIterator предполагает, что это безопасно к memcpy данный итератор во внутренний буфер и реализует его собственных участников путем передачи его собственного буфера как пустоты* к предоставленным функциям, как будто это был исходный итератор.

Контейнер затем должен реализовать статические итеративные функции, которые бросают предоставленную пустоту* к станд.:: список:: итератор. Контейнер:: начните () и Контейнер:: конец () просто создает станд.:: список:: итератор, передайте указатель на него в MagicIterator наряду с таблицей его итеративных функций и возвратите MagicIterator.

Это несколько отвратительно, и нарушает мое исходное правило относительно "никакого memcpy ()" и делает предположения о внутренностях рассматриваемых итераторов. Но это избегает выделения "кучи", сохраняет внутренности Набора (включая Объект) частными, MagicIterator рендеринга, совершенно независимый от рассматриваемого набора и от IInterface*, и в теории, позволяет MagicIterators работать с любым набором (если его итераторы могут быть безопасно memcopy () 'd).

0
ответ дан 1 December 2019 в 02:47
поделиться

Я думаю, что у Вас есть два отдельных вопроса здесь:

Во-первых, создайте итератор, который возвратится IInterface* от Вашего list<Container::Item>. Это, легко покончили 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 и возвратитесь в из begin() и end() методы.

Во-вторых, Вы хотите во время выполнения полиморфное MagicIterator. Это точно что any_iterator делает. MagicIterator<IInterface*> справедливо any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>, и cont_iter может быть просто присвоен ему.

15
ответ дан 1 December 2019 в 02:47
поделиться

Не звучит слишком сложным. Можно определить итератор снаружи. Можно также использовать определения типов. Что-то вроде этого соответствовало бы, я думаю. Обратите внимание, что это был бы путь инструмент для очистки если, что MagicIterator будет не свободным шаблоном, а членом Объекта, 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) {
    // ...
}

Можно также использовать смешивание итератора, обеспеченное повышением, которое работает как входная версия повышения:: function_output_iterator. Это называет Ваш итератор operator() который затем возвращает соответствующее значение, делая то, что мы делаем выше в нашем operator* в принципе. Вы находите его в random/detail/iterator_mixin.hpp. Это, вероятно, привело бы к меньшему количеству кода. Но это также требует для разрушения нашей шеи для обеспечения материала друга, потому что Объект является частным, и итератор не определяется в Объекте. Так или иначе, удача :)

4
ответ дан 1 December 2019 в 02:47
поделиться

Создайте краткий обзор 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 класс для переноса его:

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 с пользовательским подклассом IteratorImplementation в 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);
}
4
ответ дан 1 December 2019 в 02:47
поделиться

Я не вижу оснований, почему Вы не можете реализовать это точно, поскольку Вы разметили его... я пропускающий что-то?

Для разъяснения необходимо будет поместить некоторые методы доступа для Контейнерного класса. Они могут быть частными, и можно объявить MagicIterator как друг, если бы Вы чувствуете, что это - лучший способ инкапсулировать его, но я выставил бы их непосредственно. Эти методы доступа использовали бы нормальный итератор STL в Контейнере и выполнили бы преобразование в IInterface. Таким образом итерация была бы на самом деле сделана с методами доступа Контейнера, и MagicIterator просто будет своего рода объектом прокси помочь. Для создания этого повторно используемым Вы могли сделать, чтобы MagicIterator передал в некотором идентификаторе для поиска итератора STL в Контейнере, или у Вас мог на самом деле быть он передача в итераторе STL как a void *.

1
ответ дан 1 December 2019 в 02:47
поделиться

Это действительно зависит от Container, потому что возвращаемые значения c.Begin() и c.End() определяются реализацией.

Если список возможных Containers известен MagicIterator, класс обертки мог использоваться.

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)
{
    // ...
}

Больше информации здесь.

2
ответ дан 1 December 2019 в 02:47
поделиться

Посетитель может быть более простым (и, следовательно, более удобным в обслуживании) решением.

0
ответ дан 1 December 2019 в 02:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: