Как скрыть итератор и реализацию контейнера в пользовательском классе (без повышения)

Я ломал голову над этим последние сутки или около того. Я пытаюсь выяснить, как вернуть итератор из класса, скрывая тип контейнера, который используется классом. Например, у меня есть класс canvas, который содержит виджеты с тем же интерфейсом, и они хранятся в частном порядке в std :: vector. Так….

Упрощенный код

class Canvas
{
public:
WidgetIterator begin();
WidgetIterator end();
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};

class Canvas::Impl
{
public:
std::vector<widget> mWidget;
//how to return mWidget.begin(), mWidget.end() up as widget iterator??
}

usage:
int main()
{
Canvas canvas();
...Fill canvas with widgets...
//iterate over widgets
for(auto& a_widget : canvas)
{
//do something with / to a_widget.  User does not know or care
//that underlying iterator is std::vector<widget>::iterator
}
...Do more stuff....
return 0;
}

По сути, я хотел бы как-то использовать псевдонимы mWidget.begin () и mWidget.end () через Canvas :: begin () и Canvas :: end (). Пользователь знает, что итератор для виджета, ему просто не нужно знать, что итератор — это std :: vector :: iterator. Я пытаюсь скрыть свои реализации, используя PIMPL, и сохранить информацию о том, как вещи хранятся в классе.

Кажется, я не могу найти правильную «формулу». Я смотрел на стирание типов и пытался возвращать указатели на функции через интерфейс, но я не могу придумать способ удержать std :: vector :: iterator вне заголовка, и все, что я видел до сих пор в поискать это не похоже на то, что я пытаюсь сделать. Может ли кто-нибудь указать мне правильное направление? Материал для чтения? Есть понятие, которое я пропускаю? О-о, и я видел некоторые примеры использования, которые я не мог понять, как заставить работать в моей ситуации. Я хотел бы избежать этого, потому что я пытаюсь уменьшить внешние зависимости.

Заранее большое спасибо!!!

0

Решение

Самый простой способ удалить тип итератора — написать генератор входного итератора.

Этого достаточно для for(:) цикл, но не все другие алгоритмы, и может легко обернуть любой контейнер произвольного доступа:

template<class T>
struct gen_iterator_t {
std::function<T(std::size_t)> f;
std::size_t n = 0;
gen_iterator& operator++() { ++n; return *this; }
gen_iterator operator++(int) { auto r = *this; ++*this; return r; }
T operator*() { return f(n); }
gen_iterator_t( gen_iterator_t const& )=default;
gen_iterator_t( gen_iterator_t && )=default;
gen_iterator_t& operator=( gen_iterator_t const& )=default;
gen_iterator_t& operator=( gen_iterator_t && )=default;
gen_iterator_t()=default;

explicit operator bool() const { return static_cast<bool>(f); }

gen_iterator_t( std::function<T(std::size_t)> fin, std::size_t i ):
f(fin), n(i)
{}

friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
if (!lhs && !rhs) return true;
if (!lhs || !rhs) return false;
return lhs.n == rhs.n;
}
friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
return !(lhs==rhs);
}
};

мы тогда пишем range:

template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It s, It f ) { return {s,f}; }

и создать диапазон:

template<class F>
range_t< gen_iterator_t< std::result_of_t<F&(std::size_t)> > >
generate_range( F f, std::size_t start, std::size_t finish ) {
return { {f, start}, {f, finish} };
}

теперь ваш класс разоблачает

class Canvas {
public:
range_t< gen_iterator_t< widget& > > get_widgets();
};

который реализован как

range_t< gen_iterator_t< widget& > > Canvas::get_widgets() {
return generate_range(
[this]( std::size_t n )->widget& { return mImpl->mWidget[n]; },
0, mImpl->mWidget.size()
);
}

и мало что выставлено.

Если вы хотите пойти дальше и иметь возможность обернуть контейнеры с произвольным доступом, это немного больше работы.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]