2015-02-02 4 views
5

Предположим, у меня есть итератор C++, который не только обходит структуру данных, но также применяет преобразование к элементам, когда он разыменован.Возможно ли иметь трансформаторный Iterator в C++?

В реальном мире, например, вот итератор, который выходит за пиксели в растровом изображении, превращая растровый конкретный формат пиксель в удобную структуру:

class ConstPixelIterator { 

    public: struct Pixel { 
    float Red; 
    float Green; 
    float Blue; 
    float Alpha; 
    }; 

    public: ConstPixelIterator(const Bitmap &bitmap); 

    // ...standard iterator functionality... 

    public: Pixel operator *() { 
    // Read from memory and convert pixel format-specific bytes into Pixel structure 
    } 

}; 

Теперь, если я хотел бы реализовать Некоммерческий -const iterator (т. е. разрешить пользователю изменять пиксели), каков наилучший способ сделать это?

Некоторые идеи я считал:

  • я мог бы поставить аксессор методы в Pixel структуре вместо простых полей и дать ему ссылку на его владельца на домашний телефон. Это, однако, означает, что если пользователь изменил R, G, B и A, я бы четыре раза конвертировал пиксель в формат пикселя растрового изображения и записывал в память 4 раза.

  • Я мог бы вернуть ссылку на пиксель из итератора и предоставить ему метод Update(), который необходимо вызвать, если пиксель был изменен. Это будет неинтуитивно понятным, и пользователи риска не смогут позвонить Update.

  • Я всегда мог вернуть Pixel по значению и предоставить специальный оператор присваивания. ли сломать стандартный шаблон итератора - относящий итератора без разыменования должен переместить итератор, а не обновлять элемент он, указывая на

+0

Я думаю, что идиоматический способ заключается в том, что 'operartor *()' возвращает ссылку (возможно, const) на фактический пиксель. Естественно, итератор мог сохранить ссылку на исходный контейнер. – dmg

+0

Да, но * фактический пиксель * имеет различный формат (например, 16 бит на пиксель с 5-6-5 битами для красно-зелено-синего), и я хотел бы скрыть эту деталь от пользователя, таким образом, я 'm возвращает прокси-объект, а не * фактический пиксель *. Конечно, итератор ссылается на исходный контейнер («битмап») - моя проблема заключается в том, чтобы сообщать итератору, когда ему нужно записать изменения в прокси-объекте обратно в исходный контейнер. – Cygon

+0

Посмотрите на [Boost.Iterator] (http://www.boost.org/doc/libs/1_57_0/libs/iterator/doc/index.html) и [Boost.Range] (http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/index.html). – Angew

ответ

3

У нас есть существующий пример std::vector<bool>::iterator - который должен тянуть несколько трюков для записи в один бит.

Одним из решений является возвращение ProxyPixel. Он содержит ссылку на исходный пиксель. Вы заявили, что обновление R, G, B, A может вызвать 4 записи. Это правда и понятно. После первой записи только R, базовое изображение должно иметь обновленное значение R в конце концов.

Или вы счастливы принять окончательное обновление? В этом случае вы можете отложить обратную связь до ProxyPixel::~ProxyPixel. Да, основное изображение будет временно не синхронизировано с изменением размера прокси-пикселя, но оно будет более эффективным. Разумный компромисс.

+2

Недавно Эрик Ниблер написал очень информативное [сообщение в блоге] (http://ericniebler.com/2015/01/28/to-be-or-not-to-be-an-iterator/) о проблемах, связанных с прокси-сервером итераторы в отношении стандартных категорий итераторов (просто добавив это как FYI для OP). –

+0

Спасибо @ Björn, за сообщение в блоге и за упоминание * прокси-итератора * - теперь у меня есть имя для моей проблемы :) – Cygon

+0

Спасибо @MSalters. Я полностью забыл 'std :: vector '. Это может послужить хорошим руководством. «ProxyPixel», обновляющий его деструктор, представляет собой еще одно интересное сочетание преимуществ и проблем (даже если я хочу писать только (например, 'std :: fill()'), пиксели будут считаться первыми. И редкие обновления все равно будут писать к целому растровому изображению, итерации по нему). – Cygon

0

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

Это подталкивает закон деметатора (пиксель не должен изменять растровое изображение, чтобы установить его собственное значение); Вы не должны этого делать.

Я могу вернуть ссылку на пиксель из итератора и предоставить ему метод Update(), который необходимо вызвать, если пиксель был изменен. Это было бы неинтуитивно понятным, и пользователи-пользователи не захотели бы вызвать Update.

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

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

Не делайте этого; это нарушает принцип наименьшего удивления.

Рассмотрите возможность создания пикселя независимо от растрового изображения (то есть пиксель не знает, что такое растровое изображение).

Вы должны иметь возможность устанавливать (действительные) значения в пиксель легко (с помощью аксессуаров для полей или Update(R, G, B, A) или аналогичных) и иметь растровое изображение, знать, что такое пиксель, и как обновлять себя из пикселей, одним (или более) из них:

void Bitmap::Update(int x, int y, const Pixel& p); 
void Bitmap::Fill(const Pixel& p); 
void Bitmap::SetRange(SomeRangeObject r, const Pixel& p); 

Таким образом, операция обновления на пикселах потребуется следующие шаги:

  • получить пиксели из растрового изображения
  • обновления/преобразования пикселей при необходимости
  • обновление растровый из пикселей

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

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

+0

Действительно, это проверенное и правильное решение во многих библиотеках. Моя мотивация для итератора (или итераторного) дизайна заключалась в том, чтобы избежать проверки полных границ и расчета адресов для каждого доступа к пикселям. - Хотя, если битмап фактически не хранит экземпляры «Pixel», я не думаю, что итерационная модель была бы тривиальной - вы все равно столкнетесь с моей проблемой. Если ваше предложение не состоит в том, чтобы преобразовать растровое изображение (или его область) в матрицу «Pixel», работать над этим, а затем записать его обратно. – Cygon

+0

Я думал, что вы можете создавать экземпляры пикселей в итераторах или в общему интерфейсу Bitmap. Тогда экземпляры Pixel будут прокси/интерфейсом для редактирования пикселей. В любом случае, я не уверен, что вы можете избежать проверки границ. Рассматривали ли вы реализацию проверки границ и преобразование в матрицу пикселей как отдельную (и видимую) операцию? Работа с растровым изображением тогда будет следующими: 1. загрузить растровое изображение; 2. получить матрицу пикселей; 3. применять пиксельные преобразования; 4. Установите матрицу пикселей в растровое изображение. 5. save bitmap – utnapistim

+0

Мне нужно подумать об этом :) | Извлечение матрицы пикселей было бы жизнеспособным, хотя я все еще немного сопротивляюсь этой идее (например, накладные расходы на редкие обновления, нужно выяснить интересующую область до выполнения работы). Опять же, это полностью устранит проблему итератора прокси. | Pixel as proxy похож на мой пример # 1 (только для пояснения, в моих трех примерах, Pixel не будет знать о «Bitmap»: это вспомогательный класс с ограниченным охватом, в наиболее привлекательном случае он будет содержать ссылку вернитесь к 'PixelIterator', чтобы сообщить ему о подходящем времени для обновления). – Cygon

0

Элементы структуры пикселя могут быть реализованы как свойства.

Это позволит вам писать такие вещи, как

iter->r = 0; 

Это не позволит вам отправить пиксель-структуру для функций, которые ожидать, что это будет «straigtforward»

Реализация имущества в С ++ легко, смотри, например here

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

Смежные вопросы