Skip to content

Commit ab49962

Browse files
committed
Introduce SharedAttributableData
1 parent 998f5ad commit ab49962

8 files changed

Lines changed: 120 additions & 73 deletions

File tree

include/openPMD/CustomHierarchy.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,10 @@ class CustomHierarchy : public Container<CustomHierarchy>
150150
CustomHierarchy &operator=(CustomHierarchy const &) = default;
151151
CustomHierarchy &operator=(CustomHierarchy &&) = default;
152152

153-
Container<RecordComponent> datasets();
153+
Container<RecordComponent> &datasets();
154154

155155
template <typename ContainedType>
156-
auto asContainerOf() -> Container<ContainedType>;
156+
auto asContainerOf() -> Container<ContainedType> &;
157157

158158
Container<Mesh> meshes{};
159159
Container<ParticleSpecies> particles{};

include/openPMD/backend/Attributable.hpp

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,20 @@ class CustomHierarchy;
5454

5555
namespace internal
5656
{
57-
class AttributableData
57+
class SharedAttributableData
5858
{
5959
friend class openPMD::Attributable;
6060
friend class openPMD::CustomHierarchy;
6161

6262
public:
63-
AttributableData();
64-
AttributableData(AttributableData const &) = delete;
65-
AttributableData(AttributableData &&) = delete;
66-
virtual ~AttributableData() = default;
63+
SharedAttributableData(AttributableData *);
64+
SharedAttributableData(SharedAttributableData const &) = delete;
65+
SharedAttributableData(SharedAttributableData &&) = delete;
66+
virtual ~SharedAttributableData() = default;
6767

68-
AttributableData &operator=(AttributableData const &) = delete;
69-
AttributableData &operator=(AttributableData &&) = delete;
68+
SharedAttributableData &
69+
operator=(SharedAttributableData const &) = delete;
70+
SharedAttributableData &operator=(SharedAttributableData &&) = delete;
7071

7172
using A_MAP = std::map<std::string, Attribute>;
7273
/**
@@ -83,6 +84,41 @@ namespace internal
8384
A_MAP m_attributes;
8485
};
8586

87+
/*
88+
* This is essentially a two-level pointer.
89+
*
90+
* 1. level: Our public API hands out handles to users that are (shared)
91+
* pointers to an internal object (PIMPL).
92+
* 2. level: Multiple internal objects might refer to the same item in an
93+
* openPMD file, e.g. to the same backend object.
94+
* So, the internal object for an Attributable is a shared pointer to the
95+
* unique object identifying this item.
96+
*
97+
* Such sharing occurs in the CustomHierarchy class where multiple
98+
* containers refer to the same group in the openPMD hierarchy
99+
* (container of groups, of meshes, of particle species, of datasets).
100+
* This might also become relevant for links as in HDF5 if we choose to
101+
* implement them.
102+
*/
103+
104+
class AttributableData : public std::shared_ptr<SharedAttributableData>
105+
{
106+
friend class openPMD::Attributable;
107+
friend class openPMD::CustomHierarchy;
108+
109+
using SharedData_t = std::shared_ptr<SharedAttributableData>;
110+
111+
public:
112+
AttributableData();
113+
AttributableData(SharedAttributableData *);
114+
AttributableData(AttributableData const &) = delete;
115+
AttributableData(AttributableData &&) = delete;
116+
virtual ~AttributableData() = default;
117+
118+
AttributableData &operator=(AttributableData const &) = delete;
119+
AttributableData &operator=(AttributableData &&) = delete;
120+
};
121+
86122
template <typename, typename>
87123
class BaseRecordData;
88124
struct CustomHierarchyData;
@@ -113,7 +149,7 @@ class Attributable
113149
friend class Writable;
114150
friend class WriteIterations;
115151
friend class CustomHierarchy;
116-
friend class internal::CustomHierarchyData;
152+
friend struct internal::CustomHierarchyData;
117153

118154
protected:
119155
// tag for internal constructor
@@ -340,7 +376,7 @@ OPENPMD_protected
340376
}
341377
AbstractIOHandler const *IOHandler() const
342378
{
343-
auto &opt = m_attri->m_writable.IOHandler;
379+
auto &opt = writable().IOHandler;
344380
if (!opt || !opt->has_value())
345381
{
346382
return nullptr;
@@ -349,33 +385,33 @@ OPENPMD_protected
349385
}
350386
Writable *&parent()
351387
{
352-
return m_attri->m_writable.parent;
388+
return writable().parent;
353389
}
354390
Writable const *parent() const
355391
{
356-
return m_attri->m_writable.parent;
392+
return writable().parent;
357393
}
358394
Writable &writable()
359395
{
360-
return m_attri->m_writable;
396+
return (*m_attri)->m_writable;
361397
}
362398
Writable const &writable() const
363399
{
364-
return m_attri->m_writable;
400+
return (*m_attri)->m_writable;
365401
}
366402

367403
inline void setData(std::shared_ptr<internal::AttributableData> attri)
368404
{
369405
m_attri = std::move(attri);
370406
}
371407

372-
inline internal::AttributableData &get()
408+
inline internal::SharedAttributableData &get()
373409
{
374-
return *m_attri;
410+
return **m_attri;
375411
}
376-
inline internal::AttributableData const &get() const
412+
inline internal::SharedAttributableData const &get() const
377413
{
378-
return *m_attri;
414+
return **m_attri;
379415
}
380416

381417
bool dirty() const

include/openPMD/backend/Writable.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class Span;
4747

4848
namespace internal
4949
{
50+
class SharedAttributableData;
5051
class AttributableData;
5152
class SeriesData;
5253
} // namespace internal
@@ -63,6 +64,7 @@ namespace internal
6364
*/
6465
class Writable final
6566
{
67+
friend class internal::SharedAttributableData;
6668
friend class internal::AttributableData;
6769
friend class internal::SeriesData;
6870
friend class Attributable;
@@ -128,6 +130,11 @@ OPENPMD_private
128130
*/
129131
std::shared_ptr<std::optional<std::unique_ptr<AbstractIOHandler>>>
130132
IOHandler = nullptr;
133+
/*
134+
* Link to the containing Attributable.
135+
* If multiple Attributables share the same Writable, then the creating one.
136+
* (See SharedAttributableData)
137+
*/
131138
internal::AttributableData *attributable = nullptr;
132139
Writable *parent = nullptr;
133140
bool dirty = true;

src/CustomHierarchy.cpp

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,15 @@ namespace internal
145145
{
146146
/*
147147
* m_embeddeddatasets and its friends should point to the same instance
148-
* of Attributable Can only use a non-owning pointer in here in order to
149-
* avoid shared pointer cycles. When handing this object out to users,
150-
* we create a copy that has a proper owning pointer (see
151-
* CustomHierarchy::datasets()).
148+
* of Attributable.
152149
*/
153150
for (auto p : std::initializer_list<Attributable *>{
154151
&m_embeddedDatasets, &m_embeddedMeshes, &m_embeddedParticles})
155152
{
156-
(*p).Attributable::setData(
157-
std::shared_ptr<AttributableData>(this, [](auto const *) {}));
153+
static_cast<std::shared_ptr<internal::SharedAttributableData> &>(
154+
*p->m_attri) =
155+
static_cast<
156+
std::shared_ptr<internal::SharedAttributableData> &>(*this);
158157
}
159158
}
160159
} // namespace internal
@@ -425,12 +424,12 @@ void CustomHierarchy::synchronizeContainers(
425424
target_container.emplace(std::move(pair));
426425
}
427426
source.container().clear();
428-
auto &target_attributes = target.m_attri->m_attributes;
429-
for (auto &pair : source.m_attri->m_attributes)
427+
auto &target_attributes = target.get().m_attributes;
428+
for (auto &pair : source.get().m_attributes)
430429
{
431430
target_attributes.emplace(std::move(pair));
432431
}
433-
source.m_attri->m_attributes.clear();
432+
source.get().m_attributes.clear();
434433
source.setData(target.m_containerData);
435434
// We need to do this since we redirect the Attributable pointers for some
436435
// members:
@@ -454,14 +453,14 @@ void CustomHierarchy::flush_internal(
454453
{
455454
if (!meshes.empty())
456455
{
457-
auto defaultMeshes =
456+
auto &defaultMeshes =
458457
(*this)[defaultMeshesPath].asContainerOf<Mesh>();
459458
synchronizeContainers(defaultMeshes, meshes);
460459
}
461460

462461
if (!particles.empty())
463462
{
464-
auto defaultParticles =
463+
auto &defaultParticles =
465464
(*this)[defaultParticlesPath].asContainerOf<ParticleSpecies>();
466465
synchronizeContainers(defaultParticles, particles);
467466
}
@@ -594,54 +593,45 @@ bool CustomHierarchy::dirtyRecursive() const
594593
return false;
595594
}
596595

597-
Container<RecordComponent> CustomHierarchy::datasets()
596+
Container<RecordComponent> &CustomHierarchy::datasets()
598597
{
599-
Container<RecordComponent> res = get().m_embeddedDatasets;
600-
res.Attributable::setData(m_customHierarchyData);
601-
return res;
598+
return get().m_embeddedDatasets;
602599
}
603600

604601
template <typename ContainedType>
605-
auto CustomHierarchy::asContainerOf() -> Container<ContainedType>
602+
auto CustomHierarchy::asContainerOf() -> Container<ContainedType> &
606603
{
607604
if constexpr (std::is_same_v<ContainedType, CustomHierarchy>)
608605
{
609606
return *static_cast<Container<CustomHierarchy> *>(this);
610607
}
608+
else if constexpr (std::is_same_v<ContainedType, Mesh>)
609+
{
610+
return get().m_embeddedMeshes;
611+
}
612+
else if constexpr (std::is_same_v<ContainedType, ParticleSpecies>)
613+
{
614+
return get().m_embeddedParticles;
615+
}
616+
else if constexpr (std::is_same_v<ContainedType, RecordComponent>)
617+
{
618+
return get().m_embeddedDatasets;
619+
}
611620
else
612621
{
613-
Container<ContainedType> res = [&data = get()]() {
614-
if constexpr (std::is_same_v<ContainedType, Mesh>)
615-
{
616-
return data.m_embeddedMeshes;
617-
}
618-
else if constexpr (std::is_same_v<ContainedType, ParticleSpecies>)
619-
{
620-
return data.m_embeddedParticles;
621-
}
622-
else if constexpr (std::is_same_v<ContainedType, RecordComponent>)
623-
{
624-
return data.m_embeddedDatasets;
625-
}
626-
else
627-
{
628-
static_assert(
629-
auxiliary::dependent_false_v<ContainedType>,
630-
"[CustomHierarchy::asContainerOf] Type parameter must be "
631-
"one of: CustomHierarchy, RecordComponent, Mesh, "
632-
"ParticleSpecies.");
633-
}
634-
}();
635-
res.Attributable::setData(m_customHierarchyData);
636-
return res;
622+
static_assert(
623+
auxiliary::dependent_false_v<ContainedType>,
624+
"[CustomHierarchy::asContainerOf] Type parameter must be "
625+
"one of: CustomHierarchy, RecordComponent, Mesh, "
626+
"ParticleSpecies.");
637627
}
638628
}
639629

640630
template auto CustomHierarchy::asContainerOf<CustomHierarchy>()
641-
-> Container<CustomHierarchy>;
631+
-> Container<CustomHierarchy> &;
642632
template auto CustomHierarchy::asContainerOf<RecordComponent>()
643-
-> Container<RecordComponent>;
644-
template auto CustomHierarchy::asContainerOf<Mesh>() -> Container<Mesh>;
633+
-> Container<RecordComponent> &;
634+
template auto CustomHierarchy::asContainerOf<Mesh>() -> Container<Mesh> &;
645635
template auto CustomHierarchy::asContainerOf<ParticleSpecies>()
646-
-> Container<ParticleSpecies>;
636+
-> Container<ParticleSpecies> &;
647637
} // namespace openPMD

src/Iteration.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "openPMD/auxiliary/DerefDynamicCast.hpp"
2727
#include "openPMD/auxiliary/Filesystem.hpp"
2828
#include "openPMD/auxiliary/StringManip.hpp"
29+
#include "openPMD/backend/Attributable.hpp"
2930
#include "openPMD/backend/Writable.hpp"
3031

3132
#include <algorithm>
@@ -488,7 +489,8 @@ auto Iteration::beginStep(
488489
case IE::fileBased:
489490
if (thisObject.has_value())
490491
{
491-
file = &static_cast<Attributable &>(*thisObject).get();
492+
file = static_cast<internal::AttributableData *>(
493+
thisObject.value().m_attri.get());
492494
}
493495
else
494496
{
@@ -568,7 +570,7 @@ void Iteration::endStep()
568570
switch (series.iterationEncoding())
569571
{
570572
case IE::fileBased:
571-
file = &Attributable::get();
573+
file = m_attri.get();
572574
break;
573575
case IE::groupBased:
574576
case IE::variableBased:

src/Series.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,7 +1882,7 @@ AdvanceStatus Series::advance(
18821882
else
18831883
{
18841884
param.mode = mode;
1885-
IOTask task(&file.m_writable, param);
1885+
IOTask task(&file->m_writable, param);
18861886
IOHandler()->enqueue(task);
18871887
}
18881888

@@ -1972,7 +1972,7 @@ AdvanceStatus Series::advance(AdvanceMode mode)
19721972

19731973
Parameter<Operation::ADVANCE> param;
19741974
param.mode = mode;
1975-
IOTask task(&series.m_writable, param);
1975+
IOTask task(&series->m_writable, param);
19761976
IOHandler()->enqueue(task);
19771977

19781978
// We cannot call Series::flush now, since the IO handler is still filled
@@ -2301,8 +2301,9 @@ namespace internal
23012301
* `Series` is needlessly flushed a second time. Otherwise, error
23022302
* messages can get very confusing.
23032303
*/
2304-
if (this->m_lastFlushSuccessful && m_writable.IOHandler &&
2305-
m_writable.IOHandler->has_value())
2304+
if (this->m_lastFlushSuccessful &&
2305+
operator*().m_writable.IOHandler &&
2306+
operator*().m_writable.IOHandler->has_value())
23062307
{
23072308
Series impl;
23082309
impl.setData({this, [](auto const *) {}});
@@ -2313,9 +2314,9 @@ namespace internal
23132314
// This releases the openPMD hierarchy
23142315
iterations.container().clear();
23152316
// Release the IO Handler
2316-
if (m_writable.IOHandler)
2317+
if (operator*().m_writable.IOHandler)
23172318
{
2318-
*m_writable.IOHandler = std::nullopt;
2319+
*operator*().m_writable.IOHandler = std::nullopt;
23192320
}
23202321
}
23212322
} // namespace internal

0 commit comments

Comments
 (0)