openPMD-api
BaseRecord.hpp
1 /* Copyright 2017-2021 Fabian Koller
2  *
3  * This file is part of openPMD-api.
4  *
5  * openPMD-api is free software: you can redistribute it and/or modify
6  * it under the terms of of either the GNU General Public License or
7  * the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * openPMD-api is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License and the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * and the GNU Lesser General Public License along with openPMD-api.
19  * If not, see <http://www.gnu.org/licenses/>.
20  */
21 #pragma once
22 
23 #include "openPMD/Error.hpp"
24 #include "openPMD/RecordComponent.hpp"
25 #include "openPMD/UnitDimension.hpp"
26 #include "openPMD/auxiliary/Variant.hpp"
27 #include "openPMD/backend/BaseRecordComponent.hpp"
28 #include "openPMD/backend/Container.hpp"
29 
30 #include <array>
31 #include <stdexcept>
32 #include <string>
33 #include <type_traits> // std::remove_reference_t
34 #include <utility> // std::declval
35 
36 namespace openPMD
37 {
38 template <typename>
39 class BaseRecord;
40 namespace internal
41 {
42  template <
43  typename T_elem, // = T_RecordComponent
44  /*
45  * Technically not necessary, but some old compilers ignore friend
46  * declarations at this place, so we specify the data class explicitly
47  */
48  typename T_RecordComponentData = typename T_elem::Data_t>
49  class BaseRecordData final
50  : public ContainerData<T_elem>
51  , public T_RecordComponentData
52  {
53  using T_RecordComponent = T_elem;
54 
55  public:
57 
58  BaseRecordData(BaseRecordData const &) = delete;
59  BaseRecordData(BaseRecordData &&) = delete;
60 
61  BaseRecordData &operator=(BaseRecordData const &) = delete;
62  BaseRecordData &operator=(BaseRecordData &&) = delete;
63  };
64 
65  // @todo change T_InternalContainer to direct iterator type
66  template <
67  typename T_BaseRecord_,
68  typename T_BaseRecordData_,
69  typename T_BaseIterator>
71  {
72  /*
73  * Allow other template instantiations of ScalarIterators member access.
74  */
75  template <typename, typename, typename>
76  friend class ScalarIterator;
77 
78  using T_BaseRecord = T_BaseRecord_;
79  using T_BaseRecordData = T_BaseRecordData_;
80  using T_RecordComponent = typename T_BaseRecord::T_RecordComponent;
81  using T_Container = typename T_BaseRecord::T_Container;
82  using T_Value =
83  std::remove_reference_t<decltype(*std::declval<T_BaseIterator>())>;
84  using Left = T_BaseIterator;
85  struct Right
86  { /*Empty*/
87  constexpr bool operator==(Right const &) const noexcept
88  {
89  return true;
90  }
91  constexpr bool operator!=(Right const &) const noexcept
92  {
93  return false;
94  }
95  };
96 
97  template <typename>
98  friend class openPMD::BaseRecord;
99 
100  T_BaseRecordData *m_baseRecordData = nullptr;
101  using ScalarTuple =
102  std::optional<std::pair<std::string const, T_RecordComponent>>;
103  ScalarTuple m_scalarTuple;
104  std::variant<Left, Right> m_iterator;
105 
106  explicit ScalarIterator() = default;
107 
108  ScalarIterator(T_BaseRecord *baseRecord)
109  : m_baseRecordData(&baseRecord->get())
110  , m_scalarTuple(std::make_pair(
111  RecordComponent::SCALAR, T_RecordComponent(*baseRecord)))
112  , m_iterator(Right())
113  {}
114  ScalarIterator(T_BaseRecord *baseRecord, Left iterator)
115  : m_baseRecordData(&baseRecord->get())
116  , m_scalarTuple(std::make_pair(
117  RecordComponent::SCALAR, T_RecordComponent(*baseRecord)))
118  , m_iterator(std::move(iterator))
119  {}
120 
121  public:
134  template <
135  typename Other,
136  /*
137  * We need this in order to not accidentally register this as a copy
138  * constructor.
139  */
140  typename SFINAE = std::enable_if_t<
141  !std::is_same_v<T_BaseRecord, typename Other::T_BaseRecord>>>
142  ScalarIterator(Other const &other)
143  : m_baseRecordData(other.m_baseRecordData)
144  , m_scalarTuple(
145  other.m_scalarTuple.has_value()
146  ? ScalarTuple(std::make_pair(
147  RecordComponent::SCALAR,
148  T_RecordComponent(
149  other.m_scalarTuple.value().second)))
150  : ScalarTuple(std::nullopt))
151  , m_iterator(std::visit(
152  auxiliary::overloaded{
153  [](typename Other::Left const &left) {
154  // This converts the STL iterator to an
155  // STL const_iterator
156  return std::variant<Left, Right>(left);
157  },
158  [](typename Other::Right const &) {
159  return std::variant<Left, Right>(Right());
160  }},
161  other.m_iterator))
162  {}
163 
164  ScalarIterator &operator++()
165  {
166  std::visit(
167  auxiliary::overloaded{
168  [](Left &left) { ++left; },
169  [this](Right &) {
170  m_iterator = m_baseRecordData->m_container.end();
171  }},
172  m_iterator);
173  return *this;
174  }
175 
176  T_Value *operator->()
177  {
178  return std::visit(
179  auxiliary::overloaded{
180  [](Left &left) -> T_Value * { return left.operator->(); },
181  [this](Right &) -> T_Value * {
182  /*
183  * We cannot create this value on the fly since we only
184  * give out a pointer, so that would be use-after-free.
185  * Instead, we just keep one value around inside
186  * BaseRecordData and give it out when needed.
187  */
188  return &m_scalarTuple.value();
189  }},
190  m_iterator);
191  }
192 
193  T_Value &operator*()
194  {
195  return *operator->();
196  }
197 
198  bool operator==(ScalarIterator const &other) const
199  {
200  return this->m_iterator == other.m_iterator;
201  }
202 
203  bool operator!=(ScalarIterator const &other) const
204  {
205  return !operator==(other);
206  }
207  };
208 } // namespace internal
209 
220 template <typename T_elem>
222  : public Container<T_elem>
223  , public T_elem // T_RecordComponent
224 {
225 public:
226  using T_RecordComponent = T_elem;
228 
229 private:
230  using T_Self = BaseRecord<T_elem>;
231  friend class Iteration;
232  friend class ParticleSpecies;
233  friend class PatchRecord;
234  friend class Record;
235  friend class Mesh;
236  template <typename, typename>
237  friend class internal::BaseRecordData;
238  template <typename, typename, typename>
239  friend class internal::ScalarIterator;
240 
241  using Data_t =
243  std::shared_ptr<Data_t> m_baseRecordData;
244 
245  static_assert(
247  "Internal error: Scalar components cannot have generation policies.");
248 
249  inline Data_t const &get() const
250  {
251  return *m_baseRecordData;
252  }
253 
254  inline Data_t &get()
255  {
256  return *m_baseRecordData;
257  }
258 
259  BaseRecord();
260 
261 protected:
262  inline void setData(std::shared_ptr<Data_t> data)
263  {
264  m_baseRecordData = std::move(data);
265  T_Container::setData(m_baseRecordData);
266  T_RecordComponent::setData(m_baseRecordData);
267  }
268 
269 public:
270  using key_type = typename Container<T_elem>::key_type;
271  using mapped_type = typename Container<T_elem>::mapped_type;
272  using value_type = typename Container<T_elem>::value_type;
273  using size_type = typename Container<T_elem>::size_type;
274  using difference_type = typename Container<T_elem>::difference_type;
275  using allocator_type = typename Container<T_elem>::allocator_type;
276  using reference = typename Container<T_elem>::reference;
277  using const_reference = typename Container<T_elem>::const_reference;
278  using pointer = typename Container<T_elem>::pointer;
279  using const_pointer = typename Container<T_elem>::const_pointer;
280 
282  T_Self,
283  Data_t,
284  typename T_Container::InternalContainer::iterator>;
286  T_Self const,
287  Data_t const,
288  typename T_Container::InternalContainer::const_iterator>;
290  T_Self,
291  Data_t,
292  typename T_Container::InternalContainer::reverse_iterator>;
294  T_Self const,
295  Data_t const,
296  typename T_Container::InternalContainer::const_reverse_iterator>;
297 
298 private:
299  template <typename... Arg>
300  iterator makeIterator(Arg &&...arg)
301  {
302  return iterator{this, std::forward<Arg>(arg)...};
303  }
304  template <typename... Arg>
305  const_iterator makeIterator(Arg &&...arg) const
306  {
307  return const_iterator{this, std::forward<Arg>(arg)...};
308  }
309  template <typename... Arg>
310  reverse_iterator makeReverseIterator(Arg &&...arg)
311  {
312  return reverse_iterator{this, std::forward<Arg>(arg)...};
313  }
314  template <typename... Arg>
315  const_reverse_iterator makeReverseIterator(Arg &&...arg) const
316  {
317  return const_reverse_iterator{this, std::forward<Arg>(arg)...};
318  }
319 
320 public:
321  iterator begin()
322  {
323  if (get().m_datasetDefined)
324  {
325  return makeIterator();
326  }
327  else
328  {
329  return makeIterator(T_Container::begin());
330  }
331  }
332 
333  const_iterator begin() const
334  {
335  if (get().m_datasetDefined)
336  {
337  return makeIterator();
338  }
339  else
340  {
341  return makeIterator(T_Container::begin());
342  }
343  }
344 
345  const_iterator cbegin() const
346  {
347  if (get().m_datasetDefined)
348  {
349  return makeIterator();
350  }
351  else
352  {
353  return makeIterator(T_Container::cbegin());
354  }
355  }
356 
357  iterator end()
358  {
359  return makeIterator(T_Container::end());
360  }
361 
362  const_iterator end() const
363  {
364  return makeIterator(T_Container::end());
365  }
366 
367  const_iterator cend() const
368  {
369  return makeIterator(T_Container::cend());
370  }
371 
372  reverse_iterator rbegin()
373  {
374  if (get().m_datasetDefined)
375  {
376  return makeReverseIterator();
377  }
378  else
379  {
380  return makeReverseIterator(this->container().rbegin());
381  }
382  }
383 
384  const_reverse_iterator rbegin() const
385  {
386  if (get().m_datasetDefined)
387  {
388  return makeReverseIterator();
389  }
390  else
391  {
392  return makeReverseIterator(this->container().rbegin());
393  }
394  }
395 
396  const_reverse_iterator crbegin() const
397  {
398  if (get().m_datasetDefined)
399  {
400  return makeReverseIterator();
401  }
402  else
403  {
404  return makeReverseIterator(this->container().crbegin());
405  }
406  }
407 
408  reverse_iterator rend()
409  {
410  return makeReverseIterator(this->container().rend());
411  }
412 
413  const_reverse_iterator rend() const
414  {
415  return makeReverseIterator(this->container().rend());
416  }
417 
418  const_reverse_iterator crend() const
419  {
420  return makeReverseIterator(this->container().crend());
421  }
422 
423  virtual ~BaseRecord() = default;
424 
425  mapped_type &operator[](key_type const &key);
426  mapped_type &operator[](key_type &&key);
427  mapped_type &at(key_type const &key);
428  mapped_type const &at(key_type const &key) const;
429  size_type erase(key_type const &key);
430  iterator erase(iterator res);
431  bool empty() const noexcept;
432  iterator find(key_type const &key);
433  const_iterator find(key_type const &key) const;
434  size_type count(key_type const &key) const;
435  size_type size() const;
436  void clear();
437  std::pair<iterator, bool> insert(value_type const &value);
438  std::pair<iterator, bool> insert(value_type &&value);
439  iterator insert(const_iterator hint, value_type const &value);
440  iterator insert(const_iterator hint, value_type &&value);
441  template <class InputIt>
442  void insert(InputIt first, InputIt last);
443  void insert(std::initializer_list<value_type> ilist);
444  void swap(BaseRecord &other);
445  bool contains(key_type const &key) const;
446  template <class... Args>
447  auto emplace(Args &&...args) -> std::pair<iterator, bool>;
448 
450  // iterator erase(const_iterator first, const_iterator last) override;
451 
470  std::array<double, 7> unitDimension() const;
471 
472  void setDatasetDefined(BaseRecordComponent::Data_t &data) override
473  {
474  if (!T_Container::empty())
475  {
476  throw error::WrongAPIUsage(
477  "A scalar component can not be contained at the same time as "
478  "one or more regular components.");
479  }
480  T_RecordComponent::setDatasetDefined(data);
481  }
482 
487  bool scalar() const;
488 
489 protected:
490  void readBase();
491 
492 private:
493  void flush(std::string const &, internal::FlushParams const &) final;
494  virtual void
495  flush_impl(std::string const &, internal::FlushParams const &) = 0;
496 
497  void eraseScalar();
498 }; // BaseRecord
499 
500 // implementation
501 
502 namespace internal
503 {
504  template <typename T_elem, typename T_RecordComponentData>
505  BaseRecordData<T_elem, T_RecordComponentData>::BaseRecordData()
506  {
507  Attributable impl;
508  impl.setData({this, [](auto const *) {}});
509  impl.setAttribute(
510  "unitDimension",
511  std::array<double, 7>{{0., 0., 0., 0., 0., 0., 0.}});
512  }
513 } // namespace internal
514 
515 template <typename T_elem>
516 BaseRecord<T_elem>::BaseRecord()
517  : T_Container(Attributable::NoInit())
518  , T_RecordComponent(Attributable::NoInit())
519 {
520  setData(std::make_shared<Data_t>());
521 }
522 
523 template <typename T_elem>
524 auto BaseRecord<T_elem>::operator[](key_type const &key) -> mapped_type &
525 {
526  auto it = this->find(key);
527  if (it != this->end())
528  {
529  return std::visit(
530  auxiliary::overloaded{
531  [](typename iterator::Left &l) -> mapped_type & {
532  return l->second;
533  },
534  [this](typename iterator::Right &) -> mapped_type & {
535  /*
536  * Do not use the iterator result, as it is a non-owning
537  * handle
538  */
539  return static_cast<mapped_type &>(*this);
540  }},
541  it.m_iterator);
542  }
543  else
544  {
545  bool const keyScalar = (key == RecordComponent::SCALAR);
546  if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
547  (scalar() && !keyScalar))
548  throw error::WrongAPIUsage(
549  "A scalar component can not be contained at the same time as "
550  "one or more regular components.");
551 
552  if (keyScalar)
553  {
554  /*
555  * This activates the RecordComponent API of this object.
556  */
557  T_RecordComponent::get();
558  }
559  mapped_type &ret = keyScalar ? static_cast<mapped_type &>(*this)
560  : T_Container::operator[](key);
561  return ret;
562  }
563 }
564 
565 template <typename T_elem>
566 auto BaseRecord<T_elem>::operator[](key_type &&key) -> mapped_type &
567 {
568  auto it = this->find(key);
569  if (it != this->end())
570  {
571  return std::visit(
572  auxiliary::overloaded{
573  [](typename iterator::Left &l) -> mapped_type & {
574  return l->second;
575  },
576  [this](typename iterator::Right &) -> mapped_type & {
577  /*
578  * Do not use the iterator result, as it is a non-owning
579  * handle
580  */
581  return static_cast<mapped_type &>(*this);
582  }},
583  it.m_iterator);
584  }
585  else
586  {
587  bool const keyScalar = (key == RecordComponent::SCALAR);
588  if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
589  (scalar() && !keyScalar))
590  throw error::WrongAPIUsage(
591  "A scalar component can not be contained at the same time as "
592  "one or more regular components.");
593 
594  if (keyScalar)
595  {
596  /*
597  * This activates the RecordComponent API of this object.
598  */
599  T_RecordComponent::get();
600  }
601  mapped_type &ret = keyScalar ? static_cast<mapped_type &>(*this)
602  : T_Container::operator[](std::move(key));
603  return ret;
604  }
605 }
606 
607 template <typename T_elem>
608 auto BaseRecord<T_elem>::at(key_type const &key) -> mapped_type &
609 {
610  return const_cast<mapped_type &>(
611  static_cast<BaseRecord<T_elem> const *>(this)->at(key));
612 }
613 
614 template <typename T_elem>
615 auto BaseRecord<T_elem>::at(key_type const &key) const -> mapped_type const &
616 {
617  bool const keyScalar = (key == RecordComponent::SCALAR);
618  if (keyScalar)
619  {
620  if (!get().m_datasetDefined)
621  {
622  throw std::out_of_range(
623  "[at()] Requested scalar entry from non-scalar record.");
624  }
625  return static_cast<mapped_type const &>(*this);
626  }
627  else
628  {
629  return T_Container::at(key);
630  }
631 }
632 
633 template <typename T_elem>
634 auto BaseRecord<T_elem>::erase(key_type const &key) -> size_type
635 {
636  bool const keyScalar = (key == RecordComponent::SCALAR);
637  size_type res;
638  if (!keyScalar || (keyScalar && this->at(key).constant()))
639  res = Container<T_elem>::erase(key);
640  else
641  {
642  res = this->datasetDefined() ? 1 : 0;
643  eraseScalar();
644  }
645 
646  if (keyScalar)
647  {
648  this->written() = false;
649  this->writable().abstractFilePosition.reset();
650  this->get().m_datasetDefined = false;
651  }
652  return res;
653 }
654 
655 template <typename T_elem>
656 auto BaseRecord<T_elem>::erase(iterator it) -> iterator
657 {
658  return std::visit(
659  auxiliary::overloaded{
660  [this](typename iterator::Left &left) {
661  return makeIterator(T_Container::erase(left));
662  },
663  [this](typename iterator::Right &) {
664  eraseScalar();
665  return end();
666  }},
667  it.m_iterator);
668 }
669 
670 template <typename T_elem>
671 auto BaseRecord<T_elem>::empty() const noexcept -> bool
672 {
673  return !scalar() && T_Container::empty();
674 }
675 
676 template <typename T_elem>
677 auto BaseRecord<T_elem>::find(key_type const &key) -> iterator
678 {
679  auto &r = get();
680  if (key == RecordComponent::SCALAR && get().m_datasetDefined)
681  {
682  if (r.m_datasetDefined)
683  {
684  return begin();
685  }
686  else
687  {
688  return end();
689  }
690  }
691  else
692  {
693  return makeIterator(r.m_container.find(key));
694  }
695 }
696 
697 template <typename T_elem>
698 auto BaseRecord<T_elem>::find(key_type const &key) const -> const_iterator
699 {
700  auto &r = get();
701  if (key == RecordComponent::SCALAR && get().m_datasetDefined)
702  {
703  if (r.m_datasetDefined)
704  {
705  return begin();
706  }
707  else
708  {
709  return end();
710  }
711  }
712  else
713  {
714  return makeIterator(r.m_container.find(key));
715  }
716 }
717 
718 template <typename T_elem>
719 auto BaseRecord<T_elem>::count(key_type const &key) const -> size_type
720 {
721  if (key == RecordComponent::SCALAR)
722  {
723  return get().m_datasetDefined ? 1 : 0;
724  }
725  else
726  {
727  return T_Container::count(key);
728  }
729 }
730 
731 template <typename T_elem>
732 auto BaseRecord<T_elem>::size() const -> size_type
733 {
734  if (scalar())
735  {
736  return 1;
737  }
738  else
739  {
740  return T_Container::size();
741  }
742 }
743 
744 template <typename T_elem>
745 auto BaseRecord<T_elem>::clear() -> void
746 {
747  if (Access::READ_ONLY == this->IOHandler()->m_frontendAccess)
748  throw std::runtime_error(
749  "Can not clear a container in a read-only Series.");
750  if (scalar())
751  {
752  eraseScalar();
753  }
754  else
755  {
756  T_Container::clear_unchecked();
757  }
758 }
759 
760 namespace detail
761 {
762  constexpr char const *const NO_SCALAR_INSERT =
763  "[BaseRecord] emplace()/insert()/swap() API invalid for scalar "
764  "records. Use the Record directly as a RecordComponent.";
765 
766  template <typename BaseRecord>
767  void verifyNonscalar(BaseRecord *self)
768  {
769  if (self->scalar())
770  {
771  throw error::WrongAPIUsage(NO_SCALAR_INSERT);
772  }
773  }
774 } // namespace detail
775 
776 template <typename T_elem>
777 auto BaseRecord<T_elem>::insert(value_type const &value)
778  -> std::pair<iterator, bool>
779 {
780  detail::verifyNonscalar(this);
781  auto res = this->container().insert(value);
782  if (res.first->first == RecordComponent::SCALAR)
783  {
784  this->container().erase(res.first);
785  throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
786  }
787  return {makeIterator(std::move(res.first)), res.second};
788 }
789 
790 template <typename T_elem>
791 auto BaseRecord<T_elem>::insert(value_type &&value) -> std::pair<iterator, bool>
792 {
793  detail::verifyNonscalar(this);
794  auto res = this->container().insert(std::move(value));
795  if (res.first->first == RecordComponent::SCALAR)
796  {
797  this->container().erase(res.first);
798  throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
799  }
800  return {makeIterator(std::move(res.first)), res.second};
801 }
802 
803 template <typename T_elem>
804 auto BaseRecord<T_elem>::insert(const_iterator hint, value_type const &value)
805  -> iterator
806 {
807  detail::verifyNonscalar(this);
808  auto base_hint = std::visit(
809  auxiliary::overloaded{
810  [](typename const_iterator::Left left) { return left; },
811  [this](typename const_iterator::Right) {
812  return static_cast<BaseRecord<T_elem> const *>(this)
813  ->container()
814  .begin();
815  }},
816  hint.m_iterator);
817  auto res = this->container().insert(base_hint, value);
818  if (res->first == RecordComponent::SCALAR)
819  {
820  this->container().erase(res);
821  throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
822  }
823  return makeIterator(res);
824 }
825 
826 template <typename T_elem>
827 auto BaseRecord<T_elem>::insert(const_iterator hint, value_type &&value)
828  -> iterator
829 {
830  detail::verifyNonscalar(this);
831  auto base_hint = std::visit(
832  auxiliary::overloaded{
833  [](typename const_iterator::Left left) { return left; },
834  [this](typename const_iterator::Right) {
835  return static_cast<BaseRecord<T_elem> const *>(this)
836  ->container()
837  .begin();
838  }},
839  hint.m_iterator);
840  auto res = this->container().insert(base_hint, std::move(value));
841  if (res->first == RecordComponent::SCALAR)
842  {
843  this->container().erase(res);
844  throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
845  }
846  return makeIterator(res);
847 }
848 
849 template <typename T_elem>
850 template <typename InputIt>
851 auto BaseRecord<T_elem>::insert(InputIt first, InputIt last) -> void
852 {
853  detail::verifyNonscalar(this);
854  this->container().insert(first, last);
855  /*
856  * We skip this check as it changes the runtime of this call from
857  * O(last-first) to O(container().size()).
858  */
859  // for (auto it = this->container().begin(); it != end; ++it)
860  // {
861  // if (it->first == RecordComponent::SCALAR)
862  // {
863  // this->container().erase(it);
864  // throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
865  // }
866  // }
867 }
868 
869 template <typename T_elem>
870 auto BaseRecord<T_elem>::insert(std::initializer_list<value_type> ilist) -> void
871 {
872  detail::verifyNonscalar(this);
873  this->container().insert(std::move(ilist));
874  /*
875  * We skip this check as it changes the runtime of this call from
876  * O(last-first) to O(container().size()).
877  */
878  // for (auto it = this->container().begin(); it != end; ++it)
879  // {
880  // if (it->first == RecordComponent::SCALAR)
881  // {
882  // this->container().erase(it);
883  // throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
884  // }
885  // }
886 }
887 
888 template <typename T_elem>
889 auto BaseRecord<T_elem>::swap(BaseRecord &other) -> void
890 {
891  detail::verifyNonscalar(this);
892  detail::verifyNonscalar(&other);
893  this->container().swap(other.container());
894 }
895 
896 template <typename T_elem>
897 auto BaseRecord<T_elem>::contains(key_type const &key) const -> bool
898 {
899  if (scalar())
900  {
901  return key == RecordComponent::SCALAR;
902  }
903  else
904  {
905  return T_Container::contains(key);
906  }
907 }
908 
909 template <typename T_elem>
910 template <typename... Args>
911 auto BaseRecord<T_elem>::emplace(Args &&...args) -> std::pair<iterator, bool>
912 {
913  detail::verifyNonscalar(this);
914  auto res = this->container().emplace(std::forward<Args>(args)...);
915  if (res.first->first == RecordComponent::SCALAR)
916  {
917  this->container().erase(res.first);
918  throw error::WrongAPIUsage(detail::NO_SCALAR_INSERT);
919  }
920  return {makeIterator(std::move(res.first)), res.second};
921 }
922 
923 template <typename T_elem>
924 inline std::array<double, 7> BaseRecord<T_elem>::unitDimension() const
925 {
926  return this->getAttribute("unitDimension")
927  .template get<std::array<double, 7>>();
928 }
929 
930 template <typename T_elem>
931 inline bool BaseRecord<T_elem>::scalar() const
932 {
933  return this->datasetDefined();
934 }
935 
936 template <typename T_elem>
937 inline void BaseRecord<T_elem>::readBase()
938 {
939  using DT = Datatype;
941 
942  aRead.name = "unitDimension";
943  this->IOHandler()->enqueue(IOTask(this, aRead));
944  this->IOHandler()->flush(internal::defaultFlushParams);
945  if (auto val =
946  Attribute(*aRead.resource).getOptional<std::array<double, 7>>();
947  val.has_value())
948  this->setAttribute("unitDimension", val.value());
949  else
950  throw std::runtime_error(
951  "Unexpected Attribute datatype for 'unitDimension'");
952 
953  aRead.name = "timeOffset";
954  this->IOHandler()->enqueue(IOTask(this, aRead));
955  this->IOHandler()->flush(internal::defaultFlushParams);
956  if (*aRead.dtype == DT::FLOAT)
957  this->setAttribute(
958  "timeOffset", Attribute(*aRead.resource).get<float>());
959  else if (*aRead.dtype == DT::DOUBLE)
960  this->setAttribute(
961  "timeOffset", Attribute(*aRead.resource).get<double>());
962  // conversion cast if a backend reports an integer type
963  else if (auto val = Attribute(*aRead.resource).getOptional<double>();
964  val.has_value())
965  this->setAttribute("timeOffset", val.value());
966  else
967  throw std::runtime_error(
968  "Unexpected Attribute datatype for 'timeOffset'");
969 }
970 
971 template <typename T_elem>
972 inline void BaseRecord<T_elem>::flush(
973  std::string const &name, internal::FlushParams const &flushParams)
974 {
975  if (!this->written() && this->empty() && !this->datasetDefined())
976  throw std::runtime_error(
977  "A Record can not be written without any contained "
978  "RecordComponents: " +
979  name);
980 
981  /*
982  * Defensive programming. Normally, this error should yield as soon as
983  * possible.
984  */
985  if (scalar() && !T_Container::empty())
986  {
987  throw error::WrongAPIUsage(
988  "A scalar component can not be contained at the same time as "
989  "one or more regular components.");
990  }
991 
992  this->flush_impl(name, flushParams);
993  if (flushParams.flushLevel != FlushLevel::SkeletonOnly)
994  {
995  this->setDirty(false);
996  }
997  // flush_impl must take care to correctly set the dirty() flag so this
998  // method doesn't do it
999 }
1000 
1001 template <typename T_elem>
1002 void BaseRecord<T_elem>::eraseScalar()
1003 {
1004  if (this->written())
1005  {
1006  Parameter<Operation::DELETE_DATASET> dDelete;
1007  dDelete.name = ".";
1008  this->IOHandler()->enqueue(IOTask(this, dDelete));
1009  this->IOHandler()->flush(internal::defaultFlushParams);
1010  }
1011  auto &data = T_RecordComponent::get();
1012  data.reset();
1013  this->writable().abstractFilePosition.reset();
1014 }
1015 } // namespace openPMD
Layer to manage storage of attributes associated with file objects.
Definition: Attributable.hpp:101
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:455
Variant datatype supporting at least all formats for attributes specified in the openPMD standard.
Definition: Attribute.hpp:56
U get() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:316
std::optional< U > getOptional() const
Retrieve a stored specific Attribute and cast if convertible.
Definition: Attribute.hpp:340
Base class for any type of record (e.g.
Definition: BaseRecord.hpp:224
bool scalar() const
Returns true if this record only contains a single component.
Definition: BaseRecord.hpp:931
std::array< double, 7 > unitDimension() const
Return the physical dimension (quantity) of a record.
Definition: BaseRecord.hpp:924
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:104
size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:396
Self-contained description of a single IO operation.
Definition: IOTask.hpp:670
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:127
Container for N-dimensional, homogeneous Records.
Definition: Mesh.hpp:41
Definition: ParticleSpecies.hpp:34
Definition: PatchRecord.hpp:32
Definition: RecordComponent.hpp:120
Definition: Record.hpp:33
The API was used in an illegal way.
Definition: Error.hpp:64
Definition: Attributable.hpp:57
Definition: BaseRecordComponent.hpp:39
Definition: BaseRecord.hpp:52
Definition: Container.hpp:71
Definition: BaseRecord.hpp:71
ScalarIterator(Other const &other)
Auto-convert normal to const iterator.
Definition: BaseRecord.hpp:142
Public definitions of openPMD-api.
Definition: Date.cpp:29
Datatype
Concrete datatype of an object available at runtime.
Definition: Datatype.hpp:51
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:86
Container Element Creation Policy.
Definition: Container.hpp:52