Skip to content

Commit 1423538

Browse files
Use C++ templates for metadatum wrapper classes
This gives a cleaner separation of C++ implementation and SWIG wrapping.
1 parent eb2c638 commit 1423538

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+4983
-3631
lines changed

src/interface/image.i

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,6 @@ DEFINE_VIEW_CALLBACK(Exiv2::Image, release_ptr(&self->io());)
115115
// Convert path encoding on Windows
116116
WINDOWS_PATH(const std::string& path)
117117

118-
// Declare metadatum wrapper classes
119-
DECLARE_METADATUM_WRAPPERS(ExifData, Exifdatum)
120-
DECLARE_METADATUM_WRAPPERS(IptcData, Iptcdatum)
121-
DECLARE_METADATUM_WRAPPERS(XmpData, Xmpdatum)
122-
123118
// Simplify handling of default parameters
124119
%typemap(default) bool useCurl {$1 = true;}
125120
%ignore Exiv2::ImageFactory::createIo(std::string const &);

src/interface/metadatum.i

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,44 @@ DEPRECATE_FUNCTION(Exiv2::Metadatum::copy, true)
7676
DEPRECATE_FUNCTION(Exiv2::Metadatum::write, true)
7777

7878
// Add __str__ slot to base type
79-
%fragment("metadatum_str", "header") {
80-
static std::string metadatum_str(Exiv2::Metadatum* datum) {
81-
return datum->key() + ": " + datum->print();
82-
};
83-
}
84-
%fragment("metadatum_str");
8579
TP_STR(Exiv2::Metadatum, metadatum_str(self))
8680

81+
// Metadatum pointer template classes from metadatum_pointer.hpp
82+
%feature("docstring") MetadatumPointerBase
83+
"Base class for pointers to :class:`Metadatum` objects."
84+
85+
TP_STR(MetadatumPointerBase, self->__str__())
86+
%ignore MetadatumPointerBase::MetadatumPointerBase;
87+
%ignore MetadatumPointerBase::~MetadatumPointerBase;
88+
%ignore MetadatumPointerBase::operator*;
89+
%ignore MetadatumPointerBase::size;
90+
%ignore MetadatumPointerBase::count;
91+
%ignore MetadatumPointerBase::_invalidate;
92+
%ignore MetadatumPointerBase::__str__;
93+
94+
%ignore MetadatumPointer::MetadatumPointer;
95+
%ignore MetadatumPointer::~MetadatumPointer;
96+
%ignore MetadatumPointer::operator*;
97+
%ignore MetadatumPointer::size;
98+
%ignore MetadatumPointer::count;
99+
%ignore MetadatumPointer::_invalidate;
100+
%ignore MetadatumPointer::__str__;
101+
102+
%feature("python:slot", "tp_iter", functype="getiterfunc")
103+
MetadataIterator::__iter__;
104+
%feature("python:slot", "tp_iternext", functype="iternextfunc")
105+
MetadataIterator::__next__;
106+
%noexception MetadataIterator::__iter__;
107+
%ignore MetadataIterator::MetadataIterator;
108+
%ignore MetadataIterator::_invalidated;
109+
%ignore MetadataIterator::_ptr;
110+
KEEP_REFERENCE(MetadataIterator*)
111+
112+
%ignore MetadatumReference::MetadatumReference;
113+
114+
%ignore metadatum_str;
115+
116+
87117
%ignore Exiv2::Key::~Key;
88118
%ignore Exiv2::Key::operator=;
89119
%ignore Exiv2::Metadatum::~Metadatum;
@@ -92,3 +122,4 @@ TP_STR(Exiv2::Metadatum, metadatum_str(self))
92122
%ignore Exiv2::cmpMetadataByTag;
93123

94124
%include "exiv2/metadatum.hpp"
125+
%include "metadatum_pointer.hpp"
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/* python-exiv2 - Python interface to libexiv2
2+
* http://github.com/jim-easterbrook/python-exiv2
3+
* Copyright (C) 2025 Jim Easterbrook [email protected]
4+
*
5+
* This file is part of python-exiv2.
6+
*
7+
* python-exiv2 is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* python-exiv2 is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with python-exiv2. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
22+
#include "exiv2/exiv2.hpp"
23+
24+
25+
std::string metadatum_str(Exiv2::Metadatum* datum) {
26+
return datum->key() + ": " + datum->print();
27+
};
28+
29+
30+
class MetadatumPointerBase {
31+
protected:
32+
bool invalidated;
33+
std::string name;
34+
public:
35+
MetadatumPointerBase(): invalidated(false) {}
36+
virtual ~MetadatumPointerBase() {}
37+
virtual Exiv2::Metadatum* operator*() const = 0;
38+
bool operator==(const MetadatumPointerBase &other) const {
39+
return *other == **this;
40+
}
41+
bool operator!=(const MetadatumPointerBase &other) const {
42+
return *other != **this;
43+
}
44+
std::string __str__() {
45+
if (invalidated)
46+
return name + "<deleted data>";
47+
Exiv2::Metadatum* ptr = **this;
48+
if (!ptr)
49+
return name + "<data end>";
50+
return name + "<" + metadatum_str(ptr) + ">";
51+
}
52+
// Provide size() C++ method for buffer size check
53+
size_t size() {
54+
if (invalidated)
55+
return 0;
56+
Exiv2::Metadatum* ptr = **this;
57+
if (!ptr)
58+
return 0;
59+
return ptr->size();
60+
}
61+
#if EXIV2_VERSION_HEX < 0x001c0000
62+
// Provide count() C++ method for index bounds check
63+
long count() {
64+
if (invalidated)
65+
return 0;
66+
Exiv2::Metadatum* ptr = **this;
67+
if (!ptr)
68+
return 0;
69+
return ptr->count();
70+
}
71+
#endif
72+
// Invalidate iterator unilaterally
73+
void _invalidate() { invalidated = true; }
74+
// Invalidate iterator if what it points to has been deleted
75+
bool _invalidate(Exiv2::Metadatum& deleted) {
76+
if (&deleted == **this)
77+
invalidated = true;
78+
return invalidated;
79+
}
80+
};
81+
82+
83+
template <typename T>
84+
class MetadatumPointer: public MetadatumPointerBase {
85+
public:
86+
virtual T* operator*() const = 0;
87+
// SWIG picks up comparison operators from parent class, but not
88+
// from grandparent (non-specialised) class
89+
using MetadatumPointerBase::operator==;
90+
using MetadatumPointerBase::operator!=;
91+
// Dereference operator gives access to all datum methods
92+
T* operator->() const {
93+
T* ptr = **this;
94+
if (!ptr)
95+
throw std::runtime_error(name + " iterator is at end of data");
96+
return ptr;
97+
}
98+
};
99+
100+
using Exifdatum_pointer = MetadatumPointer<Exiv2::Exifdatum>;
101+
using Iptcdatum_pointer = MetadatumPointer<Exiv2::Iptcdatum> ;
102+
using Xmpdatum_pointer = MetadatumPointer<Exiv2::Xmpdatum>;
103+
104+
105+
template <typename I, typename T>
106+
class MetadataIterator: public MetadatumPointer<T> {
107+
private:
108+
I ptr;
109+
I end;
110+
public:
111+
MetadataIterator(I ptr, I end): ptr(ptr), end(end) {
112+
this->name = "iterator";
113+
}
114+
MetadataIterator<I, T>* __iter__() { return this; }
115+
T* __next__() {
116+
if (this->invalidated)
117+
throw std::runtime_error(
118+
"container_type changed size during iteration");
119+
if (ptr == end)
120+
return NULL;
121+
return &(*ptr++);
122+
}
123+
T* operator*() const {
124+
if (this->invalidated)
125+
throw std::runtime_error("Metadata iterator is invalid");
126+
if (ptr == end)
127+
return NULL;
128+
return &(*ptr);
129+
}
130+
// Direct access to ptr and invalidated, for use in input typemaps
131+
bool _invalidated() const { return this->invalidated; }
132+
I _ptr() const { return ptr; }
133+
};
134+
135+
using ExifData_iterator =
136+
MetadataIterator<Exiv2::ExifData::iterator, Exiv2::Exifdatum>;
137+
using IptcData_iterator =
138+
MetadataIterator<Exiv2::IptcData::iterator, Exiv2::Iptcdatum>;
139+
using XmpData_iterator =
140+
MetadataIterator<Exiv2::XmpData::iterator, Exiv2::Xmpdatum>;
141+
142+
143+
template <typename T>
144+
class MetadatumReference: public MetadatumPointer<T> {
145+
private:
146+
T* ptr;
147+
public:
148+
MetadatumReference(T* ptr): ptr(ptr) {
149+
this->name = "pointer";
150+
}
151+
T* operator*() const {
152+
if (this->invalidated)
153+
throw std::runtime_error("Metadatum reference is invalid");
154+
return ptr;
155+
}
156+
};
157+
158+
using Exifdatum_reference = MetadatumReference<Exiv2::Exifdatum>;
159+
using Iptcdatum_reference = MetadatumReference<Exiv2::Iptcdatum>;
160+
using Xmpdatum_reference = MetadatumReference<Exiv2::Xmpdatum>;

src/interface/preview.i

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,6 @@ EXV_ENABLE_FILESYSTEM_FUNCTION(Exiv2::PreviewImage::writeFile)
5959
WINDOWS_PATH(const std::string& path)
6060
WINDOWS_PATH_OUT(extension)
6161

62-
// Declare metadatum wrapper classes
63-
DECLARE_METADATUM_WRAPPERS(ExifData, Exifdatum)
64-
DECLARE_METADATUM_WRAPPERS(IptcData, Iptcdatum)
65-
DECLARE_METADATUM_WRAPPERS(XmpData, Xmpdatum)
66-
6762
// Convert getPreviewProperties result to a Python tuple
6863
%template() std::vector<Exiv2::PreviewProperties>;
6964

0 commit comments

Comments
 (0)