openPMD-api
Attributable.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/IO/AbstractIOHandler.hpp"
24 #include "openPMD/ThrowError.hpp"
25 #include "openPMD/auxiliary/OutOfRangeMsg.hpp"
26 #include "openPMD/backend/Attribute.hpp"
27 #include "openPMD/backend/Writable.hpp"
28 
29 #include <cstddef>
30 #include <exception>
31 #include <map>
32 #include <memory>
33 #include <string>
34 #include <type_traits>
35 #include <vector>
36 
37 // expose private and protected members for invasive testing
38 #ifndef OPENPMD_protected
39 #define OPENPMD_protected protected:
40 #endif
41 
42 namespace openPMD
43 {
44 namespace traits
45 {
46  template <typename T>
47  struct GenerationPolicy;
48 } // namespace traits
49 class AbstractFilePosition;
50 class Attributable;
51 class Iteration;
52 class Series;
53 
54 namespace internal
55 {
57  {
58  friend class openPMD::Attributable;
59 
60  public:
62  AttributableData(AttributableData const &) = delete;
64  virtual ~AttributableData() = default;
65 
66  AttributableData &operator=(AttributableData const &) = delete;
67  AttributableData &operator=(AttributableData &&) = delete;
68 
69  using A_MAP = std::map<std::string, Attribute>;
76 
77  private:
81  A_MAP m_attributes;
82  };
83 
84  template <typename, typename>
85  class BaseRecordData;
86 
87  class RecordComponentData;
88 } // namespace internal
89 
90 namespace debug
91 {
92  void printDirty(Series const &);
93 }
94 
101 {
102  // @todo remove unnecessary friend (wew that sounds bitter)
103  using A_MAP = std::map<std::string, Attribute>;
104  friend Writable *getWritable(Attributable *);
105  template <typename T_elem>
106  friend class BaseRecord;
107  template <typename T_elem>
108  friend class BaseRecordInterface;
109  template <typename, typename>
110  friend class internal::BaseRecordData;
111  template <typename T, typename T_key, typename T_container>
112  friend class Container;
113  template <typename T>
114  friend struct traits::GenerationPolicy;
115  friend class Iteration;
116  friend class Series;
117  friend class Writable;
118  friend class WriteIterations;
119  friend class internal::RecordComponentData;
120  friend void debug::printDirty(Series const &);
121 
122 protected:
123  // tag for internal constructor
124  struct NoInit
125  {};
126 
128  std::shared_ptr<Data_t> m_attri;
129 
130 public:
131  Attributable();
133 
134  virtual ~Attributable() = default;
135 
150  template <typename T>
151  bool setAttribute(std::string const &key, T value);
152  bool setAttribute(std::string const &key, char const value[]);
163  Attribute getAttribute(std::string const &key) const;
164 
171  bool deleteAttribute(std::string const &key);
172 
177  std::vector<std::string> attributes() const;
182  size_t numAttributes() const;
188  bool containsAttribute(std::string const &key) const;
189 
195  std::string comment() const;
202  Attributable &setComment(std::string const &comment);
203 
216  void seriesFlush(std::string backendConfig = "{}");
217 
224  struct MyPath
225  {
226  std::string directory;
227  std::string seriesName;
228  std::string seriesExtension;
237  std::vector<std::string> group;
238  Access access;
239 
241  std::string filePath() const;
243  std::string openPMDPath() const;
244  };
245 
251  MyPath myPath() const;
252 
253  // clang-format off
254 OPENPMD_protected
255  // clang-format on
256 
257  Series retrieveSeries() const;
258 
266  Iteration const &containingIteration() const;
270  void seriesFlush(internal::FlushParams const &);
271 
272  void flushAttributes(internal::FlushParams const &);
273 
274  enum ReadMode
275  {
291  };
292  void readAttributes(ReadMode);
293 
314  template <typename T>
315  T readFloatingpoint(std::string const &key) const;
337  template <typename T>
338  std::vector<T> readVectorFloatingpoint(std::string const &key) const;
339 
340  /* views into the resources held by m_writable
341  * purely for convenience so code that uses these does not have to go
342  * through m_writable-> */
343  AbstractIOHandler *IOHandler()
344  {
345  return const_cast<AbstractIOHandler *>(
346  static_cast<Attributable const *>(this)->IOHandler());
347  }
348  AbstractIOHandler const *IOHandler() const
349  {
350  auto &opt = m_attri->m_writable.IOHandler;
351  if (!opt || !opt->has_value())
352  {
353  return nullptr;
354  }
355  return &*opt->value();
356  }
357  Writable *&parent()
358  {
359  return m_attri->m_writable.parent;
360  }
361  Writable const *parent() const
362  {
363  return m_attri->m_writable.parent;
364  }
365  Writable &writable()
366  {
367  return m_attri->m_writable;
368  }
369  Writable const &writable() const
370  {
371  return m_attri->m_writable;
372  }
373 
374  inline void setData(std::shared_ptr<internal::AttributableData> attri)
375  {
376  m_attri = std::move(attri);
377  }
378 
379  inline internal::AttributableData &get()
380  {
381  return *m_attri;
382  }
383  inline internal::AttributableData const &get() const
384  {
385  return *m_attri;
386  }
387 
388  bool dirty() const
389  {
390  return writable().dirtySelf;
391  }
394  bool dirtyRecursive() const
395  {
396  return writable().dirtyRecursive;
397  }
398  void setDirty(bool dirty_in)
399  {
400  auto &w = writable();
401  w.dirtySelf = dirty_in;
402  setDirtyRecursive(dirty_in);
403  }
404  /* Amortized O(1) if dirty_in is true, else O(1).
405  *
406  * Must be used carefully with `dirty_in == false` since it is assumed that
407  * all children are not dirty.
408  *
409  * Invariant of dirtyRecursive:
410  * this->dirtyRecursive implies parent->dirtyRecursive.
411  *
412  * Hence:
413  *
414  * * If dirty_in is true: This needs only go up far enough until a parent is
415  * found that itself is dirtyRecursive.
416  * * If dirty_in is false: Only sets `this` to `dirtyRecursive == false`.
417  * The caller must ensure that the invariant holds (e.g. clearing
418  * everything during flushing or reading logic).
419  */
420  void setDirtyRecursive(bool dirty_in)
421  {
422  auto &w = writable();
423  w.dirtyRecursive = dirty_in;
424  if (dirty_in)
425  {
426  auto current = w.parent;
427  while (current && !current->dirtyRecursive)
428  {
429  current->dirtyRecursive = true;
430  current = current->parent;
431  }
432  }
433  }
434  bool written() const
435  {
436  return writable().written;
437  }
438  bool &written()
439  {
440  return writable().written;
441  }
442 
443 private:
449  virtual void linkHierarchy(Writable &w);
450 }; // Attributable
451 
452 // note: we explicitly instantiate Attributable::setAttributeImpl for all T in
453 // Datatype in Attributable.cpp
454 template <typename T>
455 inline bool Attributable::setAttribute(std::string const &key, T value)
456 {
457  auto &attri = get();
458  if (IOHandler() &&
459  IOHandler()->m_seriesStatus == internal::SeriesStatus::Default &&
460  Access::READ_ONLY == IOHandler()->m_frontendAccess)
461  {
462  auxiliary::OutOfRangeMsg const out_of_range_msg(
463  "Attribute", "can not be set (read-only).");
464  error::throwNoSuchAttribute(out_of_range_msg(key));
465  }
466 
467  setDirty(true);
468  auto it = attri.m_attributes.lower_bound(key);
469  if (it != attri.m_attributes.end() &&
470  !attri.m_attributes.key_comp()(key, it->first))
471  {
472  // key already exists in map, just replace the value
473  it->second = Attribute(std::move(value));
474  return true;
475  }
476  else
477  {
478  // emplace a new map element for an unknown key
479  attri.m_attributes.emplace_hint(
480  it, std::make_pair(key, Attribute(std::move(value))));
481  return false;
482  }
483 }
484 
485 inline bool
486 Attributable::setAttribute(std::string const &key, char const value[])
487 {
488  return this->setAttribute(key, std::string(value));
489 }
490 
491 template <typename T>
492 inline T Attributable::readFloatingpoint(std::string const &key) const
493 {
494  static_assert(
495  std::is_floating_point<T>::value,
496  "Type of attribute must be floating point");
497 
498  return getAttribute(key).get<T>();
499 }
500 
501 template <typename T>
502 inline std::vector<T>
503 Attributable::readVectorFloatingpoint(std::string const &key) const
504 {
505  static_assert(
506  std::is_floating_point<T>::value,
507  "Type of attribute must be floating point");
508 
509  return getAttribute(key).get<std::vector<T> >();
510 }
511 } // namespace openPMD
Interface for communicating between logical and physically persistent data.
Definition: AbstractIOHandler.hpp:186
Layer to manage storage of attributes associated with file objects.
Definition: Attributable.hpp:101
std::vector< T > readVectorFloatingpoint(std::string const &key) const
Retrieve a vector of values of a floating point Attributes of user-defined precision with ensured typ...
Definition: Attributable.hpp:503
bool containsAttribute(std::string const &key) const
Check whether am Attribute with a given key exists.
Definition: Attributable.cpp:101
MyPath myPath() const
The path to this object within its containing Series.
Definition: Attributable.cpp:221
ReadMode
Definition: Attributable.hpp:275
@ FullyReread
Remove all attributes that have been read previously and read everything that the backend currently h...
Definition: Attributable.hpp:290
@ IgnoreExisting
Don't read an attribute from the backend if it has been previously read.
Definition: Attributable.hpp:280
@ OverrideExisting
Read all the attributes that the backend has to offer and override if it has been read previously.
Definition: Attributable.hpp:285
std::vector< std::string > attributes() const
List all currently stored Attributes' keys.
Definition: Attributable.cpp:85
Attribute getAttribute(std::string const &key) const
Retrieve value of Attribute stored with provided key.
Definition: Attributable.cpp:55
void seriesFlush(std::string backendConfig="{}")
Flush the corresponding Series object.
Definition: Attributable.cpp:118
bool dirtyRecursive() const
O(1).
Definition: Attributable.hpp:394
T readFloatingpoint(std::string const &key) const
Retrieve the value of a floating point Attribute of user-defined precision with ensured type-safety.
Definition: Attributable.hpp:492
size_t numAttributes() const
Count all currently stored Attributes.
Definition: Attributable.cpp:96
bool deleteAttribute(std::string const &key)
Remove Attribute of provided value both logically and physically.
Definition: Attributable.cpp:65
bool setAttribute(std::string const &key, T value)
Populate Attribute of provided name with provided value.
Definition: Attributable.hpp:455
Attributable & setComment(std::string const &comment)
Populate Attribute corresponding to a comment with the user-supplied comment.
Definition: Attributable.cpp:112
std::string comment() const
Retrieve a user-supplied comment associated with the object.
Definition: Attributable.cpp:107
Iteration const & containingIteration() const
Returns the corresponding Iteration.
Definition: Attributable.cpp:147
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
Base class for any type of record (e.g.
Definition: BaseRecord.hpp:224
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:104
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:127
Implementation for the root level of the openPMD hierarchy.
Definition: Series.hpp:219
Layer to mirror structure of logical data and persistent data in file.
Definition: Writable.hpp:75
bool written
Whether a Writable has been written to the backend.
Definition: Writable.hpp:184
bool dirtyRecursive
Tracks if there are unwritten changes anywhere in the tree whose ancestor this Writable is.
Definition: Writable.hpp:163
bool dirtySelf
Tracks if there are unwritten changes for this specific Writable.
Definition: Writable.hpp:150
Writing side of the streaming API.
Definition: WriteIterations.hpp:67
Return an error string for read-only access.
Definition: OutOfRangeMsg.hpp:37
Definition: Attributable.hpp:57
Writable m_writable
The Writable associated with this Attributable.
Definition: Attributable.hpp:75
Definition: BaseRecord.hpp:52
Definition: RecordComponent.hpp:58
@ Default
Mutability of objects in the openPMD object model is determined by the open mode (Access enum),...
Public definitions of openPMD-api.
Definition: Date.cpp:29
Access
File access mode to use during IO.
Definition: Access.hpp:30
@ READ_ONLY
Open Series as read-only, fails if Series is not found.
String serialization to describe an Attributable.
Definition: Attributable.hpp:225
std::string filePath() const
Reconstructs a path that can be passed to a Series constructor.
Definition: Attributable.cpp:196
std::string seriesName
e.g., samples/git-samples/
Definition: Attributable.hpp:227
std::string seriesExtension
e.g., dataT
Definition: Attributable.hpp:228
std::vector< std::string > group
e.g., .bp, .h5, .json, ...
Definition: Attributable.hpp:237
std::string openPMDPath() const
Return the path ob the object within the openPMD file.
Definition: Attributable.cpp:201
Definition: Attributable.hpp:125
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:86
Container Element Creation Policy.
Definition: Container.hpp:52