Skip to content

Commit f80d693

Browse files
Improved iterator invalidation detection
Added an 'invalidated' member to allow invalidation to be treated differently from normal end of iteration status. Validity is checked in the C++ class (and an exception thrown if an invalid iterator is dereferenced) rather than in a SWIG typemap (with cludgy way to bypass it in some methods).
1 parent 38e6300 commit f80d693

10 files changed

Lines changed: 167 additions & 2058 deletions

File tree

src/interface/shared/data_iterator.i

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
%ignore container_type##_iterator::size;
3434
%ignore container_type##_iterator::##container_type##_iterator;
3535
%ignore container_type##_iterator::operator*;
36-
%ignore container_type##_iterator::valid;
3736
%feature("docstring") container_type##_iterator "
3837
Python wrapper for an :class:`" #container_type "` iterator. It has most of
3938
the methods of :class:`" #datum_type "` allowing easy access to the
@@ -51,18 +50,20 @@ KEEP_REFERENCE(container_type##_iterator*)
5150
%inline %{
5251
// Base class implements all methods except dereferencing
5352
class container_type##_iterator {
54-
protected:
53+
private:
5554
Exiv2::container_type::iterator ptr;
5655
Exiv2::container_type::iterator end;
56+
bool invalidated;
5757
public:
5858
container_type##_iterator(Exiv2::container_type::iterator ptr,
5959
Exiv2::container_type::iterator end) {
6060
this->ptr = ptr;
6161
this->end = end;
62+
invalidated = false;
6263
}
6364
container_type##_iterator* __iter__() { return this; }
6465
Exiv2::datum_type* __next__() {
65-
if (!valid())
66+
if (ptr == end)
6667
return NULL;
6768
Exiv2::datum_type* result = &(*ptr);
6869
ptr++;
@@ -76,30 +77,31 @@ public:
7677
return *other != ptr;
7778
}
7879
std::string __str__() {
79-
if (valid())
80-
return "iterator<" + ptr->key() + ": " + ptr->print() + ">";
81-
return "iterator<end>";
80+
if (invalidated)
81+
return "invalid iterator";
82+
if (ptr == end)
83+
return "iterator<end>";
84+
return "iterator<" + ptr->key() + ": " + ptr->print() + ">";
8285
}
83-
bool valid() { return ptr != end; }
8486
// Provide method to invalidate iterator
85-
void _invalidate() { ptr = end; }
87+
void _invalidate() { invalidated = true; }
8688
// Provide size() C++ method for buffer size check
8789
size_t size() {
88-
if (valid())
89-
return ptr->size();
90-
return 0;
90+
if (invalidated || ptr == end)
91+
return 0;
92+
return ptr->size();
9193
}
9294
// Dereference operator gives access to all datum methods
93-
Exiv2::datum_type* operator->() const { return &(*ptr); }
95+
Exiv2::datum_type* operator->() const {
96+
if (invalidated)
97+
throw std::runtime_error(
98+
"container_type changed size during iteration");
99+
if (ptr == end)
100+
throw std::runtime_error(
101+
"container_type iterator is at end of data");
102+
return &(*ptr);
103+
}
94104
};
95-
// Bypass validity check for some methods
96-
#define NOCHECK_delete_##container_type##_iterator
97-
#define NOCHECK_##container_type##_iterator___iter__
98-
#define NOCHECK_##container_type##_iterator___next__
99-
#define NOCHECK_##container_type##_iterator___eq__
100-
#define NOCHECK_##container_type##_iterator___ne__
101-
#define NOCHECK_##container_type##_iterator___str__
102-
#define NOCHECK_##container_type##_iterator__invalidate
103105
%}
104106
%enddef // DATA_ITERATOR_CLASSES
105107

@@ -144,15 +146,6 @@ public:
144146
argp->_invalidate();
145147
#endif
146148
}
147-
// Check validity of iterator before dereferencing
148-
%typemap(check) container_type##_iterator* self {
149-
%#ifndef NOCHECK_##$symname
150-
if (!$1->valid()) {
151-
SWIG_exception_fail(SWIG_ValueError, "in method '" "$symname"
152-
"', invalid iterator cannot be dereferenced");
153-
}
154-
%#endif
155-
}
156149

157150
// Functions to store weak references to iterators (swig >= v4.4)
158151
%fragment("iterator_weakref_funcs", "header", fragment="private_data") {
@@ -227,11 +220,9 @@ static int store_iterator_weakref(PyObject* py_self, PyObject* iterator) {
227220
tmp, arg1->end());
228221
$typemap(out, container_type##_iterator*);
229222
#if SWIG_VERSION >= 0x040400
230-
if ($1->valid()) {
231-
// Keep weak reference to the Python iterator
232-
if (store_iterator_weakref(self, $result)) {
233-
SWIG_fail;
234-
}
223+
// Keep weak reference to the Python iterator
224+
if (store_iterator_weakref(self, $result)) {
225+
SWIG_fail;
235226
}
236227
#endif // SWIG_VERSION
237228
}

0 commit comments

Comments
 (0)