|
20 | 20 | */ |
21 | 21 | #include "openPMD/backend/Attributable.hpp" |
22 | 22 | #include "openPMD/Iteration.hpp" |
| 23 | +#include "openPMD/ParticleSpecies.hpp" |
| 24 | +#include "openPMD/RecordComponent.hpp" |
23 | 25 | #include "openPMD/Series.hpp" |
24 | 26 | #include "openPMD/auxiliary/DerefDynamicCast.hpp" |
25 | 27 | #include "openPMD/auxiliary/StringManip.hpp" |
| 28 | +#include "openPMD/backend/Attribute.hpp" |
26 | 29 |
|
27 | 30 | #include <algorithm> |
28 | 31 | #include <complex> |
@@ -505,4 +508,53 @@ void Attributable::linkHierarchy(Writable &w) |
505 | 508 | writable().parent = &w; |
506 | 509 | setDirty(true); |
507 | 510 | } |
| 511 | + |
| 512 | +namespace internal |
| 513 | +{ |
| 514 | + template <typename T> |
| 515 | + T &makeOwning(T &self, Series s) |
| 516 | + { |
| 517 | + /* |
| 518 | + * `self` is a handle object such as RecordComponent or Mesh (see |
| 519 | + * instantiations below). |
| 520 | + * These objects don't normally keep alive the Series, i.e. as soon as |
| 521 | + * the Series is destroyed, the handle becomes invalid. |
| 522 | + * This function modifies the handle such that it actually keeps the |
| 523 | + * Series alive and behaves otherwise identically. |
| 524 | + * First, get the internal shared pointer of the handle. |
| 525 | + */ |
| 526 | + std::shared_ptr<typename T::Data_t> data_ptr = self.T::getShared(); |
| 527 | + auto raw_ptr = data_ptr.get(); |
| 528 | + /* |
| 529 | + * Now, create a new shared pointer pointing to the same address as the |
| 530 | + * actual pointer and replace the old internal shared pointer by the new |
| 531 | + * one. |
| 532 | + */ |
| 533 | + self.setData(std::shared_ptr<typename T::Data_t>{ |
| 534 | + raw_ptr, |
| 535 | + /* |
| 536 | + * Here comes the main trick. |
| 537 | + * The new shared pointer stores (and thus keeps alive) two items |
| 538 | + * via lambda capture in its destructor: |
| 539 | + * 1. The old shared pointer. |
| 540 | + * 2. The Series. |
| 541 | + * It's important to notice that these two items are only stored |
| 542 | + * within the newly created handle, and not internally within the |
| 543 | + * actual openPMD object model. This means that no reference cycles |
| 544 | + * can occur. |
| 545 | + */ |
| 546 | + [s_lambda = std::move(s), |
| 547 | + data_ptr_lambda = std::move(data_ptr)](auto const *) { |
| 548 | + /* no-op, the lambda captures simply go out of scope */ |
| 549 | + }}); |
| 550 | + return self; |
| 551 | + } |
| 552 | + |
| 553 | + template RecordComponent &makeOwning(RecordComponent &, Series); |
| 554 | + template MeshRecordComponent &makeOwning(MeshRecordComponent &, Series); |
| 555 | + template Mesh &makeOwning(Mesh &, Series); |
| 556 | + template Record &makeOwning(Record &, Series); |
| 557 | + template ParticleSpecies &makeOwning(ParticleSpecies &, Series); |
| 558 | + template Iteration &makeOwning(Iteration &, Series); |
| 559 | +} // namespace internal |
508 | 560 | } // namespace openPMD |
0 commit comments