Skip to content

Commit 195afc0

Browse files
committed
Implement move semantics for xml::attributes
This allows to move them cheaply (or at least cheaper than by copying everything). Switch to representing null pimpl_ pointer for representing the "empty" state, as is more compatible with move semantics, but this required adding checks for the pointer being null in a number of places.
1 parent 9860554 commit 195afc0

File tree

5 files changed

+119
-23
lines changed

5 files changed

+119
-23
lines changed

include/xmlwrapp/attributes.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ struct node_impl;
7070
7171
The iterator classes allow you to access one XML attribute. This is done
7272
using the xml::attributes::attr class interface.
73+
74+
Objects of this class are copyable and movable, with the latter being more
75+
efficient, but leaving the source object in a special state in which only
76+
asignment operator and empty() and size() member functions can be used on
77+
it.
7378
*/
7479
class XMLWRAPP_API attributes
7580
{
@@ -89,6 +94,15 @@ class XMLWRAPP_API attributes
8994
*/
9095
attributes(const attributes& other);
9196

97+
/**
98+
Move construct a xml::attributes object.
99+
100+
@param other The xml::attributes object to move from.
101+
102+
@since 0.10.0
103+
*/
104+
attributes(attributes&& other);
105+
92106
/**
93107
Copy the given xml::attributes object into this one.
94108
@@ -97,6 +111,16 @@ class XMLWRAPP_API attributes
97111
*/
98112
attributes& operator=(const attributes& other);
99113

114+
/**
115+
Move the given xml::attributes object into this one.
116+
117+
@param other The xml::attributes object to move from.
118+
@return *this.
119+
120+
@since 0.10.0
121+
*/
122+
attributes& operator=(attributes&& other);
123+
100124
/**
101125
Swap this xml::attributes object with another one.
102126
@@ -162,7 +186,9 @@ class XMLWRAPP_API attributes
162186

163187
iterator();
164188
iterator(const iterator& other);
189+
iterator(iterator&& other);
165190
iterator& operator=(const iterator& other);
191+
iterator& operator=(iterator&& other);
166192
~iterator();
167193

168194
reference operator*() const;
@@ -203,8 +229,10 @@ class XMLWRAPP_API attributes
203229

204230
const_iterator();
205231
const_iterator(const const_iterator& other);
232+
const_iterator(const_iterator&& other);
206233
const_iterator(const iterator& other);
207234
const_iterator& operator=(const const_iterator& other);
235+
const_iterator& operator=(const_iterator&& other);
208236
~const_iterator();
209237

210238
reference operator*() const;

src/libxml/ait_impl.cxx

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ ait_impl ait_impl::operator++(int)
132132
// ------------------------------------------------------------------------
133133

134134
attributes::iterator::iterator()
135-
: pimpl_{new ait_impl(nullptr, nullptr)}
136135
{
137136
}
138137

@@ -163,6 +162,10 @@ attributes::iterator& attributes::iterator::operator=(const iterator& other)
163162
}
164163

165164

165+
attributes::iterator::iterator (iterator&& other) = default;
166+
attributes::iterator& attributes::iterator::operator=(iterator&& other) = default;
167+
168+
166169
void attributes::iterator::swap(iterator& other)
167170
{
168171
std::swap(pimpl_, other.pimpl_);
@@ -210,7 +213,6 @@ attributes::iterator attributes::iterator::operator++(int)
210213
// ------------------------------------------------------------------------
211214

212215
attributes::const_iterator::const_iterator()
213-
: pimpl_{new ait_impl(nullptr, nullptr)}
214216
{
215217
}
216218

@@ -247,6 +249,10 @@ attributes::const_iterator& attributes::const_iterator::operator=(const const_it
247249
}
248250

249251

252+
attributes::const_iterator::const_iterator(const_iterator&& other) = default;
253+
attributes::const_iterator& attributes::const_iterator::operator=(const_iterator&& other) = default;
254+
255+
250256
void attributes::const_iterator::swap(const_iterator& other)
251257
{
252258
std::swap(pimpl_, other.pimpl_);
@@ -355,7 +361,7 @@ const char* attributes::attr::get_value() const
355361

356362
bool operator==(const attributes::iterator& lhs, const attributes::iterator& rhs)
357363
{
358-
return *(lhs.pimpl_) == *(rhs.pimpl_);
364+
return ait_impl::are_equal(lhs.pimpl_.get(), rhs.pimpl_.get());
359365
}
360366

361367
bool operator!=(const attributes::iterator& lhs, const attributes::iterator& rhs)
@@ -365,7 +371,7 @@ bool operator!=(const attributes::iterator& lhs, const attributes::iterator& rhs
365371

366372
bool operator==(const attributes::const_iterator& lhs, const attributes::const_iterator& rhs)
367373
{
368-
return *(lhs.pimpl_) == *(rhs.pimpl_);
374+
return ait_impl::are_equal(lhs.pimpl_.get(), rhs.pimpl_.get());
369375
}
370376

371377
bool operator!=(const attributes::const_iterator& lhs, const attributes::const_iterator& rhs)
@@ -414,16 +420,35 @@ xmlAttributePtr find_default_prop(xmlNodePtr xmlnode, const char *name)
414420
return nullptr;
415421
}
416422

417-
bool operator==(const ait_impl& lhs, const ait_impl& rhs)
423+
bool ait_impl::are_equal(const ait_impl* lhs, const ait_impl* rhs)
418424
{
419-
if (lhs.fake_ || rhs.fake_)
420-
return false;
421-
return lhs.xmlattr_ == rhs.xmlattr_;
422-
}
425+
xmlAttrPtr lhsAttr;
426+
if (lhs)
427+
{
428+
if (lhs->fake_)
429+
return false;
423430

424-
bool operator!=(const ait_impl& lhs, const ait_impl& rhs)
425-
{
426-
return !(lhs == rhs);
431+
lhsAttr = lhs->xmlattr_;
432+
}
433+
else
434+
{
435+
lhsAttr = nullptr;
436+
}
437+
438+
xmlAttrPtr rhsAttr;
439+
if (rhs)
440+
{
441+
if (rhs->fake_)
442+
return false;
443+
444+
rhsAttr = rhs->xmlattr_;
445+
}
446+
else
447+
{
448+
rhsAttr = nullptr;
449+
}
450+
451+
return lhsAttr == rhsAttr;
427452
}
428453

429454
} // namespace impl

src/libxml/ait_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ class ait_impl
6161
ait_impl& operator++();
6262
ait_impl operator++(int);
6363

64-
friend bool operator==(const ait_impl& lhs, const ait_impl& rhs);
65-
friend bool operator!=(const ait_impl& lhs, const ait_impl& rhs);
64+
// Pointer arguments may be null, representing invalid attribute.
65+
static bool are_equal(const ait_impl* lhs, const ait_impl* rhs);
6666

6767
private:
6868
xmlNodePtr xmlnode_;

src/libxml/attributes.cxx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,27 @@ attributes::attributes()
112112

113113

114114
attributes::attributes(int)
115-
: pimpl_{new pimpl(nullptr)}
116115
{
117116
}
118117

119118

120119
attributes::attributes(const attributes& other)
121-
: pimpl_{new pimpl(*other.pimpl_)}
122120
{
121+
if (other.pimpl_)
122+
pimpl_.reset(new pimpl(*other.pimpl_));
123123
}
124124

125125

126126
attributes& attributes::operator=(const attributes& other)
127127
{
128-
attributes tmp(other);
129-
swap(tmp);
128+
pimpl_.reset(other.pimpl_ ? new pimpl(*other.pimpl_) : nullptr);
130129
return *this;
131130
}
132131

132+
attributes::attributes(attributes&& other) = default;
133+
134+
attributes& attributes::operator=(attributes&& other) = default;
135+
133136

134137
void attributes::swap(attributes& other)
135138
{
@@ -150,9 +153,16 @@ void attributes::set_data(void *node)
150153
{
151154
auto x = static_cast<xmlNodePtr>(node);
152155

153-
pimpl_->free_node_if_owned();
154-
pimpl_->owner_ = false;
155-
pimpl_->xmlnode_ = x;
156+
if (pimpl_)
157+
{
158+
pimpl_->free_node_if_owned();
159+
pimpl_->owner_ = false;
160+
pimpl_->xmlnode_ = x;
161+
}
162+
else
163+
{
164+
pimpl_.reset(new pimpl(x));
165+
}
156166
}
157167

158168

@@ -241,15 +251,15 @@ void attributes::erase(const char *name)
241251

242252
bool attributes::empty() const
243253
{
244-
return pimpl_->xmlnode_->properties == nullptr;
254+
return !pimpl_ || pimpl_->xmlnode_->properties == nullptr;
245255
}
246256

247257

248258
attributes::size_type attributes::size() const
249259
{
250260
size_type count = 0;
251261

252-
xmlAttrPtr prop = pimpl_->xmlnode_->properties;
262+
xmlAttrPtr prop = pimpl_ ? pimpl_->xmlnode_->properties : nullptr;
253263
while (prop != nullptr)
254264
{
255265
++count;

tests/attributes/test_attributes.cxx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,36 @@ TEST_CASE_METHOD( SrcdirConfig, "attributes/compare_attr_iterators", "[attribute
296296
CHECK( ci == i );
297297
CHECK( !(ci != i) );
298298
}
299+
300+
/*
301+
* Test to see if moving attribute objects works.
302+
*/
303+
TEST_CASE_METHOD( SrcdirConfig, "attributes/move", "[attributes]" )
304+
{
305+
xml::tree_parser parser(test_file_path("attributes/data/06b.xml").c_str());
306+
307+
xml::attributes attrs1 = parser.get_document().get_root_node().get_attributes();
308+
CHECK( !attrs1.empty() );
309+
310+
xml::attributes attrs2{std::move(attrs1)};
311+
CHECK( attrs1.empty() );
312+
CHECK( !attrs2.empty() );
313+
314+
xml::attributes attrs3;
315+
attrs3 = std::move(attrs2);
316+
CHECK( attrs2.empty() );
317+
CHECK( !attrs3.empty() );
318+
319+
auto const end = xml::attributes::iterator{};
320+
auto i1 = attrs3.begin();
321+
CHECK( i1 != end );
322+
323+
auto i2{std::move(i1)};
324+
CHECK( i1 == end );
325+
CHECK( i2 != end );
326+
327+
decltype(i2) i3;
328+
i3 = std::move(i2);
329+
CHECK( i2 == end );
330+
CHECK( i3 != end );
331+
}

0 commit comments

Comments
 (0)