Создайте корректный итератор для API записи в указатель.

Мне нужно создать итератор для API, в котором используются только средства доступа «запись в указатель» в старом стиле. Рассматриваемый API - это OGR; один из рассматриваемых классов — OGRLineString (Для справки: http://www.gdal.org/classOGRLineString.html ). Этот класс хранит ряд точек, доступ к которым можно получить с помощью следующего метода получения:

 void OGRLineString::getPoint(int pos, OGRPoint *out)

Для использования аксессора создается новый объект OGRPoint и передается указатель на него методу, который записывает данные в выделенный объект. Например:

OGRPoint *p = new OGRPoint();
lineString->getPoint(0, p);

Теперь я хотел бы реализовать итератор (подобный STL). Даже если я размещу везде большие предупреждающие знаки, утверждающие, что предоставленные OGRPoint не поддаются изменению (т.е. const) и не будут обновляться, если другой фрагмент кода изменит итерируемый OGRLineString, я получаю проблему с утечкой памяти с OGRPoint const &operator*() const, потому что API требует, чтобы я передал специально выделенный экземпляр OGRPoint, но итератор должен был выделить его. Кроме того, OGRPoints, возвращаемые итератором, не должны удаляться при удалении самого итератора. Кроме того, OGRLineString хранит не фактические экземпляры OGRPoint, которые копируются для getPoint, а простые структуры, хранящие координаты x/y/z; вся необходимая дополнительная информация (например, пространственная привязка) копируется в метод доступа. Таким образом, простой #define private public взлом не поможет.

Есть ли разумный/чистый способ добавить итератор без изменения исходного источника OGRLineString? Например, есть ли способ добавить функции в исходный класс или изменить его, как это сделала бы функция «обезьяньих исправлений» в Ruby? Или следить за временем жизни контейнера, чтобы очистить OGRPoint экземпляров, возвращенных итератором?


person Technaton    schedule 15.01.2015    source источник
comment
Вы должны посмотреть, как работает vector<bool>::operator[], это покажет вам, как решить эту причуду.   -  person Mooing Duck    schedule 15.01.2015
comment
Что вы делаете сейчас, что приводит к утечке памяти? Почему итератор не управляет OGRPoint? Что делает ваш итератор?   -  person Mooing Duck    schedule 15.01.2015
comment
можно ли использовать OGRPoint с семантикой значений? т.е. у него есть копи-конструктор и т.д.? В этом случае сделайте это вместо «нового». если нет, используйте умные указатели   -  person sp2danny    schedule 15.01.2015


Ответы (2)


Это предполагает, что OGRPoint является копируемым. Если нет, используйте умные указатели.

#include <iterator>

#include <ogr_geometry.h>

struct OGR_SimpleCurve_Points_Iterator : std::iterator< std::random_access_iterator_tag, const OGRPoint >
{
    OGR_SimpleCurve_Points_Iterator( OGRSimpleCurve* curve=nullptr, int index=0 )
        : curve(curve), index(index) {}

    OGR_SimpleCurve_Points_Iterator& operator++() { ++index; return *this; }
    OGR_SimpleCurve_Points_Iterator operator++(int) { OGR_SimpleCurve_Points_Iterator ret(*this); ++index; return ret; }
    OGR_SimpleCurve_Points_Iterator& operator--() { --index; return *this; }
    OGR_SimpleCurve_Points_Iterator operator--(int) { OGR_SimpleCurve_Points_Iterator ret(*this); --index; return ret; }

    OGR_SimpleCurve_Points_Iterator& operator+=(int n) { index+=n; return *this; }
    OGR_SimpleCurve_Points_Iterator& operator-=(int n) { index-=n; return *this; }

    OGR_SimpleCurve_Points_Iterator operator+(int n) { return OGR_SimpleCurve_Points_Iterator{curve,index+n}; }
    OGR_SimpleCurve_Points_Iterator operator-(int n) { return OGR_SimpleCurve_Points_Iterator{curve,index-n}; }

    int operator-(const OGR_SimpleCurve_Points_Iterator& other) { return index-other.index; }

    OGRPoint operator*() { OGRPoint p; curve->getPoint(index,&p); return p; }

    OGRPoint operator[](int ofs) { OGRPoint p; curve->getPoint(index+ofs,&p); return p; }

    bool operator == ( const OGR_SimpleCurve_Points_Iterator& other ) { return index==other.index; }
    bool operator != ( const OGR_SimpleCurve_Points_Iterator& other ) { return index!=other.index; }
    bool operator  > ( const OGR_SimpleCurve_Points_Iterator& other ) { return index >other.index; }
    bool operator >= ( const OGR_SimpleCurve_Points_Iterator& other ) { return index>=other.index; }
    bool operator  < ( const OGR_SimpleCurve_Points_Iterator& other ) { return index <other.index; }
    bool operator <= ( const OGR_SimpleCurve_Points_Iterator& other ) { return index<=other.index; }

private:
    OGRSimpleCurve* curve;
    int index;
};

OGR_SimpleCurve_Points_Iterator begin( OGRSimpleCurve* curve )
{
    return OGR_SimpleCurve_Points_Iterator{curve};
}

OGR_SimpleCurve_Points_Iterator end( OGRSimpleCurve* curve )
{
    return OGR_SimpleCurve_Points_Iterator{curve,curve->getNumPoints()};
}
person sp2danny    schedule 15.01.2015
comment
Большое спасибо! После некоторой работы (и чтения тонкостей, связанных с C++), эта версия лучше всего подходила для моих нужд. Замечание о копируемом, общем и необработанном указателе, наконец, заставило меня действительно понять проблему, с которой я столкнулся. Спасибо еще раз! - person Technaton; 21.01.2015
comment
Это выглядит как полный итератор, но без operator-> этот итератор соответствует только концепции output_iterator. Также отсутствуют все typedefs. Если добавить их, конструктор по умолчанию и operator+(int,iterator), то он может получить полный random_access_iterator_tag. stackoverflow.com/a/8054856/845092 - person Mooing Duck; 21.01.2015

class this_is_my_iterator;

class OutputOGRPoint {
    explicit OutputOGRPoint(this_is_my_iterator* parent_)
        :parent(parent_), p(new OGRPoint()) 
    {}
    ~OutputOGRPoint();
    operator OGRPoint *() {return p;}

    OutputOGRPoint(OutputOGRPoint &&)=default;
    OutputOGRPoint&operator=(OutputOGRPoint &&)=default;
private:    
    this_is_my_iterator* parent;
    std::unique_ptr<OGRPoint> p;
};

class this_is_my_iterator {
    OutputOGRPoint operator*()(return OutputOGRPoint(this);}
private:
    friend OutputOGRPoint;
    void apply_operator_star_changes(OGRPoint *p);
};

inline OutputOGRPoint::~OutputOGRPoint()
{parent->apply_operator_star_changes(p);}

Этот тип возвращаемого значения "псевдоуказатель" используется, когда вам нужен код, управляющий временем существования возвращаемого значения. Его также можно использовать для «возврата изменяемой ссылки» для внутренних членов, которые на самом деле не существуют. vector<bool> использует внутренние битовые поля вместо объектов bool, но использует тот же шаблон для возврата изменяемой ссылки из operator[].

person Mooing Duck    schedule 15.01.2015
comment
Концепция выходного указателя была для меня новой, большое спасибо! Я многому научился благодаря вашему ответу и ответу sp2danny. Только позже я увидел, что я также зависел от класса Factory OGR (для чтения/записи WKT/WKB), поэтому выходной указатель казался более навязчивым. Тем не менее, спасибо за предложение, которое расширило мои знания C++! - person Technaton; 21.01.2015