openPMD-api
Container.H
1 /* Copyright 2018-2021 Axel Huebl
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  * The function `bind_container` is based on std_bind.h in pybind11
22  * Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
23  *
24  * BSD-style license, see pybind11 LICENSE file.
25  */
26 #pragma once
27 
28 #include "openPMD/backend/Container.hpp"
29 #include "openPMD/backend/MeshRecordComponent.hpp"
30 #include "openPMD/backend/PatchRecord.hpp"
31 #include "openPMD/backend/PatchRecordComponent.hpp"
32 
33 #include "openPMD/binding/python/Common.hpp"
34 
35 #include <cstddef>
36 #include <memory>
37 #include <sstream>
38 #include <string>
39 #include <utility>
40 
41 namespace openPMD
42 {
43 /* based on std_bind.h in pybind11
44  *
45  * Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
46  *
47  * BSD-style license, see pybind11 LICENSE file.
48  */
49 template <typename Map, typename... Args>
50 py::class_<Map, std::unique_ptr<Map>, Args...>
51 declare_container(py::handle scope, std::string const &name)
52 {
53  using holder_type = std::unique_ptr<Map>;
54  using KeyType = typename Map::key_type;
55  using MappedType = typename Map::mapped_type;
56  using Class_ = py::class_<Map, holder_type, Args...>;
57 
58  // If either type is a non-module-local bound type then make the map
59  // binding non-local as well; otherwise (e.g. both types are either
60  // module-local or converting) the map will be module-local.
61  auto tinfo = py::detail::get_type_info(typeid(MappedType));
62  bool local = !tinfo || tinfo->module_local;
63  if (local)
64  {
65  tinfo = py::detail::get_type_info(typeid(KeyType));
66  local = !tinfo || tinfo->module_local;
67  }
68 
69  Class_ cl(scope, name.c_str(), py::module_local(local));
70 
71  // cl.def(py::init<Map const &>());
72 
73  // Register stream insertion operator (if possible)
74  py::detail::map_if_insertion_operator<Map, Class_>(cl, name);
75 
76  cl.def(
77  "__bool__",
78  [](const Map &m) -> bool { return !m.empty(); },
79  "Check whether the container is nonempty");
80 
81  cl.def(
82  "__iter__",
83  [](Map &m) { return py::make_key_iterator(m.begin(), m.end()); },
84  // keep container alive while iterator exists
85  py::keep_alive<0, 1>());
86 
87  cl.def("__repr__", [name](Map const &m) {
88  std::stringstream stream;
89  stream << "<openPMD." << name << " with ";
90  if (size_t num_entries = m.size(); num_entries == 1)
91  {
92  stream << "1 entry and ";
93  }
94  else
95  {
96  stream << num_entries << " entries and ";
97  }
98 
99  stream << m.numAttributes() << " attribute(s)>";
100  return stream.str();
101  });
102 
103  return cl;
104 }
105 
106 template <typename Map, typename Class_>
107 Class_ finalize_container(Class_ cl)
108 {
109  using KeyType = typename Map::key_type;
110  using MappedType = typename Map::mapped_type;
111 
112  cl.def(
113  "items",
114  [](Map &m) { return py::make_iterator(m.begin(), m.end()); },
115  // keep container alive while iterator exists
116  py::keep_alive<0, 1>());
117 
118  // keep same policy as Container class: missing keys are created
119  cl.def(
120  "__getitem__",
121  [](Map &m, KeyType const &k) -> MappedType & { return m[k]; },
122  // copy + keepalive
123  // All objects in the openPMD object model are handles, so using a copy
124  // is safer and still performant.
125  py::return_value_policy::copy);
126 
127  // Assignment provided only if the type is copyable
128  py::detail::map_assignment<Map, Class_>(cl);
129 
130  cl.def("__delitem__", [](Map &m, KeyType const &k) {
131  auto it = m.find(k);
132  if (it == m.end())
133  throw py::key_error();
134  m.erase(it);
135  });
136 
137  cl.def("__len__", &Map::size);
138 
139  cl.def("_ipython_key_completions_", [](Map &m) {
140  auto l = py::list();
141  for (const auto &myPair : m)
142  l.append(myPair.first);
143  return l;
144  });
145 
146  return cl;
147 }
148 } // namespace openPMD
Public definitions of openPMD-api.
Definition: Date.cpp:29