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