openPMD-api
Container.hpp
1 /* Copyright 2017-2021 Fabian Koller and Franz Poeschel
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/backend/Attributable.hpp"
24 
25 #include <initializer_list>
26 #include <map>
27 #include <set>
28 #include <stdexcept>
29 #include <string>
30 #include <type_traits>
31 #include <utility>
32 
33 // expose private and protected members for invasive testing
34 #ifndef OPENPMD_protected
35 #define OPENPMD_protected protected
36 #endif
37 
38 namespace openPMD
39 {
40 namespace traits
41 {
48  template <typename U>
49  struct GenerationPolicy
50  {
51  template <typename T>
52  void operator()(T &)
53  {}
54  };
55 } // namespace traits
56 
57 namespace internal
58 {
59  class SeriesData;
60 }
61 
62 namespace detail
63 {
64  /*
65  * This converts the key (first parameter) to its string name within the
66  * openPMD hierarchy.
67  * If the key is found to be equal to RecordComponent::SCALAR, the parentKey
68  * will be returned, adding RecordComponent::SCALAR to its back.
69  * Reason: Scalar record components do not link their containing record as
70  * parent, but rather the parent's parent, so the own key within the
71  * "apparent" parent must be given as two steps.
72  */
73  template <typename T>
74  std::vector<std::string>
75  keyAsString(T &&key, std::vector<std::string> const &parentKey)
76  {
77  (void)parentKey;
78  return {std::to_string(std::forward<T>(key))};
79  }
80 
81  // moved to a *.cpp file so we don't need to include RecordComponent.hpp
82  // here
83  template <>
84  std::vector<std::string> keyAsString<std::string const &>(
85  std::string const &key, std::vector<std::string> const &parentKey);
86 
87  template <>
88  std::vector<std::string> keyAsString<std::string>(
89  std::string &&key, std::vector<std::string> const &parentKey);
90 } // namespace detail
91 
101 template <
102  typename T,
103  typename T_key = std::string,
104  typename T_container = std::map<T_key, T> >
106 {
107  static_assert(
108  std::is_base_of<AttributableInterface, T>::value,
109  "Type of container element must be derived from Writable");
110 
111  friend class Iteration;
112  friend class ParticleSpecies;
113  friend class internal::SeriesData;
114  friend class SeriesInterface;
115  friend class Series;
116 
117 protected:
118  using InternalContainer = T_container;
119 
120 public:
121  using key_type = typename InternalContainer::key_type;
122  using mapped_type = typename InternalContainer::mapped_type;
123  using value_type = typename InternalContainer::value_type;
124  using size_type = typename InternalContainer::size_type;
125  using difference_type = typename InternalContainer::difference_type;
126  using allocator_type = typename InternalContainer::allocator_type;
127  using reference = typename InternalContainer::reference;
128  using const_reference = typename InternalContainer::const_reference;
129  using pointer = typename InternalContainer::pointer;
130  using const_pointer = typename InternalContainer::const_pointer;
131  using iterator = typename InternalContainer::iterator;
132  using const_iterator = typename InternalContainer::const_iterator;
133 
134  Container(Container const &) = default;
135  virtual ~Container() = default;
136 
137  iterator begin() noexcept
138  {
139  return m_container->begin();
140  }
141  const_iterator begin() const noexcept
142  {
143  return m_container->begin();
144  }
145  const_iterator cbegin() const noexcept
146  {
147  return m_container->cbegin();
148  }
149 
150  iterator end() noexcept
151  {
152  return m_container->end();
153  }
154  const_iterator end() const noexcept
155  {
156  return m_container->end();
157  }
158  const_iterator cend() const noexcept
159  {
160  return m_container->cend();
161  }
162 
163  bool empty() const noexcept
164  {
165  return m_container->empty();
166  }
167 
168  size_type size() const noexcept
169  {
170  return m_container->size();
171  }
172 
179  void clear()
180  {
181  if (Access::READ_ONLY == IOHandler()->m_frontendAccess)
182  throw std::runtime_error(
183  "Can not clear a container in a read-only Series.");
184 
185  clear_unchecked();
186  }
187 
188  std::pair<iterator, bool> insert(value_type const &value)
189  {
190  return m_container->insert(value);
191  }
192  template <class P>
193  std::pair<iterator, bool> insert(P &&value)
194  {
195  return m_container->insert(value);
196  }
197  iterator insert(const_iterator hint, value_type const &value)
198  {
199  return m_container->insert(hint, value);
200  }
201  template <class P>
202  iterator insert(const_iterator hint, P &&value)
203  {
204  return m_container->insert(hint, value);
205  }
206  template <class InputIt>
207  void insert(InputIt first, InputIt last)
208  {
209  m_container->insert(first, last);
210  }
211  void insert(std::initializer_list<value_type> ilist)
212  {
213  m_container->insert(ilist);
214  }
215 
216  void swap(Container &other)
217  {
218  m_container->swap(other.m_container);
219  }
220 
221  mapped_type &at(key_type const &key)
222  {
223  return m_container->at(key);
224  }
225  mapped_type const &at(key_type const &key) const
226  {
227  return m_container->at(key);
228  }
229 
240  virtual mapped_type &operator[](key_type const &key)
241  {
242  auto it = m_container->find(key);
243  if (it != m_container->end())
244  return it->second;
245  else
246  {
247  if (Access::READ_ONLY == IOHandler()->m_frontendAccess)
248  {
249  auxiliary::OutOfRangeMsg const out_of_range_msg;
250  throw std::out_of_range(out_of_range_msg(key));
251  }
252 
253  T t = T();
254  t.linkHierarchy(writable());
255  auto &ret = m_container->insert({key, std::move(t)}).first->second;
256  ret.writable().ownKeyWithinParent =
257  detail::keyAsString(key, writable().ownKeyWithinParent);
259  gen(ret);
260  return ret;
261  }
262  }
273  virtual mapped_type &operator[](key_type &&key)
274  {
275  auto it = m_container->find(key);
276  if (it != m_container->end())
277  return it->second;
278  else
279  {
280  if (Access::READ_ONLY == IOHandler()->m_frontendAccess)
281  {
282  auxiliary::OutOfRangeMsg out_of_range_msg;
283  throw std::out_of_range(out_of_range_msg(key));
284  }
285 
286  T t = T();
287  t.linkHierarchy(writable());
288  auto &ret = m_container->insert({key, std::move(t)}).first->second;
289  ret.writable().ownKeyWithinParent = detail::keyAsString(
290  std::move(key), writable().ownKeyWithinParent);
292  gen(ret);
293  return ret;
294  }
295  }
296 
297  iterator find(key_type const &key)
298  {
299  return m_container->find(key);
300  }
301  const_iterator find(key_type const &key) const
302  {
303  return m_container->find(key);
304  }
305 
311  size_type count(key_type const &key) const
312  {
313  return m_container->count(key);
314  }
315 
322  bool contains(key_type const &key) const
323  {
324  return m_container->find(key) != m_container->end();
325  }
326 
335  virtual size_type erase(key_type const &key)
336  {
337  if (Access::READ_ONLY == IOHandler()->m_frontendAccess)
338  throw std::runtime_error(
339  "Can not erase from a container in a read-only Series.");
340 
341  auto res = m_container->find(key);
342  if (res != m_container->end() && res->second.written())
343  {
345  pDelete.path = ".";
346  IOHandler()->enqueue(IOTask(&res->second, pDelete));
347  IOHandler()->flush(internal::defaultFlushParams);
348  }
349  return m_container->erase(key);
350  }
351 
353  virtual iterator erase(iterator res)
354  {
355  if (Access::READ_ONLY == IOHandler()->m_frontendAccess)
356  throw std::runtime_error(
357  "Can not erase from a container in a read-only Series.");
358 
359  if (res != m_container->end() && res->second.written())
360  {
362  pDelete.path = ".";
363  IOHandler()->enqueue(IOTask(&res->second, pDelete));
364  IOHandler()->flush(internal::defaultFlushParams);
365  }
366  return m_container->erase(res);
367  }
369  // virtual iterator erase(const_iterator first, const_iterator last)
370 
371  template <class... Args>
372  auto emplace(Args &&...args)
373  -> decltype(InternalContainer().emplace(std::forward<Args>(args)...))
374  {
375  return m_container->emplace(std::forward<Args>(args)...);
376  }
377 
378  OPENPMD_protected:
379  Container()
380  : m_container{std::make_shared< InternalContainer >()}
381  {}
382 
383  void clear_unchecked()
384  {
385  if (written())
386  throw std::runtime_error(
387  "Clearing a written container not (yet) implemented.");
388 
389  m_container->clear();
390  }
391 
392  virtual void
393  flush(std::string const &path, internal::FlushParams const &flushParams)
394  {
395  if (!written())
396  {
398  pCreate.path = path;
399  IOHandler()->enqueue(IOTask(this, pCreate));
400  }
401 
402  flushAttributes(flushParams);
403  }
404 
405  std::shared_ptr<InternalContainer> m_container;
406 
416  {
417  std::set<key_type> m_accessedKeys;
418  /*
419  * Note: Putting a copy here leads to weird bugs due to destructors
420  * being called too eagerly upon destruction.
421  * Should be avoidable by extending the frontend redesign to the
422  * Container class template
423  * (https://github.com/openPMD/openPMD-api/pull/886)
424  */
425  Container &m_originalContainer;
426 
427  public:
428  explicit EraseStaleEntries(Container &container_in)
429  : m_originalContainer(container_in)
430  {}
431 
432  template <typename K>
433  mapped_type &operator[](K &&k)
434  {
435  m_accessedKeys.insert(k); // copy
436  return m_originalContainer[std::forward<K>(k)];
437  }
438 
439  template <typename K>
440  mapped_type &at(K &&k)
441  {
442  m_accessedKeys.insert(k); // copy
443  return m_originalContainer.at(std::forward<K>(k));
444  }
445 
451  template <typename K>
452  void forget(K &&k)
453  {
454  m_accessedKeys.erase(std::forward<K>(k));
455  }
456 
458  {
459  auto &map = *m_originalContainer.m_container;
460  using iterator_t = typename InternalContainer::const_iterator;
461  std::vector<iterator_t> deleteMe;
462  deleteMe.reserve(map.size() - m_accessedKeys.size());
463  for (iterator_t it = map.begin(); it != map.end(); ++it)
464  {
465  auto lookup = m_accessedKeys.find(it->first);
466  if (lookup == m_accessedKeys.end())
467  {
468  deleteMe.push_back(it);
469  }
470  }
471  for (auto &it : deleteMe)
472  {
473  map.erase(it);
474  }
475  }
476  };
477 
478  EraseStaleEntries eraseStaleEntries()
479  {
480  return EraseStaleEntries(*this);
481  }
482 };
483 
484 } // namespace openPMD
Return an error string for read-only access.
Definition: OutOfRangeMsg.hpp:36
Self-contained description of a single IO operation.
Definition: IOTask.hpp:615
This class wraps a Container and forwards operator[]() and at() to it.
Definition: Container.hpp:415
virtual mapped_type & operator[](key_type const &key)
Access the value that is mapped to a key equivalent to key, creating it if such key does not exist al...
Definition: Container.hpp:240
Container Element Creation Policy.
Definition: Attributable.hpp:46
Logical compilation of data from one snapshot (e.g.
Definition: Iteration.hpp:40
auto emplace(Args &&...args) -> decltype(InternalContainer().emplace(std::forward< Args >(args)...))
Definition: Container.hpp:372
bool contains(key_type const &key) const
Checks if there is an element with a key equivalent to an exiting key in the container.
Definition: Container.hpp:322
void forget(K &&k)
Remove key from the list of accessed keys.
Definition: Container.hpp:452
virtual size_type erase(key_type const &key)
Remove a single element from the container and (if written) from disk.
Definition: Container.hpp:335
Root level of the openPMD hierarchy.
Definition: Series.hpp:537
Definition: Container.cpp:50
Public definitions of openPMD-api.
Definition: Date.cpp:28
void clear()
Remove all objects from the container and (if written) from disk.
Definition: Container.hpp:179
Data members for Series.
Definition: Series.hpp:63
Parameters recursively passed through the openPMD hierarchy when flushing.
Definition: AbstractIOHandler.hpp:103
Implementation for the root level of the openPMD hierarchy.
Definition: Series.hpp:110
size_type count(key_type const &key) const
This returns either 1 if the key is found in the container of 0 if not.
Definition: Container.hpp:311
virtual iterator erase(iterator res)
Definition: Container.hpp:353
Definition: ParticleSpecies.hpp:33
Map-like container that enforces openPMD requirements and handles IO.
Definition: Container.hpp:105
Definition: Attributable.hpp:444
open series as read-only, fails if series is not found
virtual mapped_type & operator[](key_type &&key)
Access the value that is mapped to a key equivalent to key, creating it if such key does not exist al...
Definition: Container.hpp:273