-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathContainer.H
More file actions
165 lines (143 loc) · 5.1 KB
/
Container.H
File metadata and controls
165 lines (143 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* Copyright 2018-2021 Axel Huebl
*
* This file is part of openPMD-api.
*
* openPMD-api is free software: you can redistribute it and/or modify
* it under the terms of of either the GNU General Public License or
* the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* openPMD-api is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with openPMD-api.
* If not, see <http://www.gnu.org/licenses/>.
*
* The function `bind_container` is based on std_bind.h in pybind11
* Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
*
* BSD-style license, see pybind11 LICENSE file.
*/
#pragma once
#include "openPMD/backend/Container.hpp"
#include "openPMD/backend/MeshRecordComponent.hpp"
#include "openPMD/backend/PatchRecord.hpp"
#include "openPMD/backend/PatchRecordComponent.hpp"
#include "openPMD/binding/python/Common.hpp"
#include <cstddef>
#include <memory>
#include <optional>
#include <pybind11/attr.h>
#include <sstream>
#include <string>
namespace openPMD
{
/* based on std_bind.h in pybind11
*
* Copyright (c) 2016 Sergey Lyskov and Wenzel Jakob
*
* BSD-style license, see pybind11 LICENSE file.
*/
template <typename Map, typename... Args>
py::class_<Map, std::unique_ptr<Map>, Args...> declare_container(
py::handle scope,
std::string const &name,
std::optional<std::string> docstring = std::nullopt)
{
using holder_type = std::unique_ptr<Map>;
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;
using Class_ = py::class_<Map, holder_type, Args...>;
// If either type is a non-module-local bound type then make the map
// binding non-local as well; otherwise (e.g. both types are either
// module-local or converting) the map will be module-local.
auto tinfo = py::detail::get_type_info(typeid(MappedType));
bool local = !tinfo || tinfo->module_local;
if (local)
{
tinfo = py::detail::get_type_info(typeid(KeyType));
local = !tinfo || tinfo->module_local;
}
Class_ cl = docstring.has_value()
? Class_(
scope, name.c_str(), docstring->c_str(),
py::module_local(local))
: Class_(scope, name.c_str(), py::module_local(local));
// cl.def(py::init<Map const &>());
// Register stream insertion operator (if possible)
py::detail::map_if_insertion_operator<Map, Class_>(cl, name);
cl.def(
"__bool__",
[](const Map &m) -> bool { return !m.empty(); },
"Check whether the container is nonempty");
cl.def(
"__iter__",
[](Map &m) { return py::make_key_iterator(m.begin(), m.end()); },
// keep container alive while iterator exists
py::keep_alive<0, 1>());
// overwrite to avoid that the __len__ of Attributable is used
cl.def(
"__len__",
[](const Map &m) { return m.size(); },
"Number of elements in the container to iterate.");
cl.def("__repr__", [name](Map const &m) {
std::stringstream stream;
stream << "<openPMD." << name << " with ";
if (size_t num_entries = m.size(); num_entries == 1)
{
stream << "1 entry and ";
}
else
{
stream << num_entries << " entries and ";
}
stream << m.numAttributes() << " attribute(s)>";
return stream.str();
});
return cl;
}
template <typename Map, typename Class_>
Class_ finalize_container(Class_ cl, bool skip_getitem = false)
{
using KeyType = typename Map::key_type;
using MappedType = typename Map::mapped_type;
cl.def(
"items",
[](Map &m) { return py::make_iterator(m.begin(), m.end()); },
// keep container alive while iterator exists
py::keep_alive<0, 1>());
if (!skip_getitem)
{
// keep same policy as Container class: missing keys are created
cl.def(
"__getitem__",
[](Map &m, KeyType const &k) -> MappedType { return m[k]; },
// copy + keepalive
// All objects in the openPMD object model are handles, so using a
// copy is safer and still performant.
py::return_value_policy::move,
py::keep_alive<0, 1>());
}
// Assignment provided only if the type is copyable
py::detail::map_assignment<Map, Class_>(cl);
cl.def("__delitem__", [](Map &m, KeyType const &k) {
auto it = m.find(k);
if (it == m.end())
throw py::key_error();
m.erase(it);
});
cl.def("__len__", &Map::size);
cl.def("_ipython_key_completions_", [](Map &m) {
auto l = py::list();
for (const auto &myPair : m)
l.append(myPair.first);
return l;
});
return cl;
}
} // namespace openPMD