ndarray
NumPy-friendly multidimensional arrays in C++
Loading...
Searching...
No Matches
pybind11.h
Go to the documentation of this file.
1/*
2 * LSST Data Management System
3 * Copyright 2008-2016 AURA/LSST.
4 *
5 * This product includes software developed by the
6 * LSST Project (http://www.lsst.org/).
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the LSST License Statement and
19 * the GNU General Public License along with this program. If not,
20 * see <https://www.lsstcorp.org/LegalNotices/>.
21 */
22
23#ifndef NDARRAY_pybind11_h_INCLUDED
24#define NDARRAY_pybind11_h_INCLUDED
25
44#include "pybind11.h"
45#include "pybind11/numpy.h"
46
47#include "ndarray.h"
48#include "ndarray/eigen.h"
49
50namespace ndarray {
51
52namespace detail {
53
54inline void destroyCapsule(PyObject * p) {
55 void * m = PyCapsule_GetPointer(p, "ndarray.Manager");
56 Manager::Ptr * b = reinterpret_cast<Manager::Ptr*>(m);
57 delete b;
58}
59
60} // namespace ndarray::detail
61
62inline PyObject* makePyManager(Manager::Ptr const & m) {
63 return PyCapsule_New(
64 new Manager::Ptr(m),
65 "ndarray.Manager",
66 detail::destroyCapsule
67 );
68}
69
70#if PYBIND11_VERSION_MAJOR == 2 && PYBIND11_VERSION_MINOR <= 1
71using pybind11_np_size_t = size_t;
72#else
73using pybind11_np_size_t = ssize_t;
74#endif
75
76template <typename T, int N, int C>
77struct
78#ifdef __GNUG__
79// pybind11 hides all symbols in its namespace only when this is set,
80// and in that case we should hide these classes too.
81__attribute__((visibility("hidden")))
82#endif
84 using Element = typename ndarray::Array<T,N,C>::Element;
85 using Wrapper = pybind11::array_t<typename std::remove_const<Element>::type, 0>; // 0: no ensurecopy
86 static constexpr bool isConst = std::is_const<Element>::value;
87
88 Pybind11Helper() : isNone(false), wrapper() {}
89
90 bool init(pybind11::handle src) {
91 isNone = src.is_none();
92 if (isNone) {
93 return true;
94 }
95 if (!Wrapper::check_(src)) {
96 return false;
97 }
98 try {
99 wrapper = pybind11::reinterpret_borrow<Wrapper>(src);
100 } catch (pybind11::error_already_set & err) {
101 return false;
102 }
103 return true;
104 }
105
106 bool check() const {
107 if (isNone) {
108 return true;
109 }
110 if (!wrapper) {
111 return false;
112 }
113 if (wrapper.ndim() != N) {
114 return false;
115 }
116 if (!isConst && !wrapper.writeable()) {
117 return false;
118 }
119 pybind11_np_size_t const * shape = wrapper.shape();
120 pybind11_np_size_t const * strides = wrapper.strides();
121 pybind11_np_size_t const itemsize = wrapper.itemsize();
122 if (C > 0) {
123 // If the shape is zero in any dimension, we don't
124 // worry about the strides.
125 for (int i = 0; i < C; ++i) {
126 if (shape[N-i-1] == 0) {
127 return true;
128 }
129 }
130 pybind11_np_size_t requiredStride = itemsize;
131 for (int i = 0; i < C; ++i) {
132 if (strides[N-i-1] != requiredStride) {
133 return false;
134 }
135 requiredStride *= shape[N-i-1];
136 }
137 } else if (C < 0) {
138 // If the shape is zero in any dimension, we don't
139 // worry about the strides.
140 for (int i = 0; i < -C; ++i) {
141 if (shape[i] == 0) {
142 return true;
143 }
144 }
145 pybind11_np_size_t requiredStride = itemsize;
146 for (int i = 0; i < -C; ++i) {
147 if (strides[i] != requiredStride) {
148 return false;
149 }
150 requiredStride *= shape[i];
151 }
152 }
153 return true;
154 }
155
156 ndarray::Array<T,N,C> convert() const {
157 if (isNone) {
158 return ndarray::Array<T,N,C>();
159 }
160 if (!pybind11::reinterpret_borrow<pybind11::bool_>(wrapper.dtype().attr("isnative"))) {
161 PyErr_SetString(PyExc_TypeError, "Only arrays with native byteorder can be converted to C++.");
162 throw pybind11::error_already_set();
163 }
166 pybind11_np_size_t const * pShape = wrapper.shape();
167 pybind11_np_size_t const * pStrides = wrapper.strides();
168 pybind11_np_size_t const itemsize = wrapper.itemsize();
169 for (int i = 0; i < N; ++i) {
170 if (pStrides[i] % itemsize != 0) {
171 PyErr_SetString(
172 PyExc_TypeError,
173 "Cannot convert array to C++: strides must be an integer multiple of the element size"
174 );
175 throw pybind11::error_already_set();
176 }
177 nShape[i] = pShape[i];
178 nStrides[i] = pStrides[i]/itemsize;
179 }
181 ndarray::external(const_cast<Element*>(wrapper.data()),
182 nShape, nStrides, pybind11::object(wrapper))
183 );
184 }
185
186 static pybind11::handle toPython(ndarray::Array<T,N,C> const & src) {
187 Vector<Size,N> nShape = src.getShape();
188 Vector<Offset,N> nStrides = src.getStrides();
189 std::vector<pybind11_np_size_t> pShape(N);
190 std::vector<pybind11_np_size_t> pStrides(N);
191 pybind11_np_size_t const itemsize = sizeof(Element);
192 for (int i = 0; i < N; ++i) {
193 pShape[i] = nShape[i];
194 pStrides[i] = nStrides[i]*itemsize;
195 }
196 pybind11::object base;
197 if (src.getManager()) {
198 base = pybind11::reinterpret_steal<pybind11::object>(ndarray::makePyManager(src.getManager()));
199 }
200 Wrapper result(pShape, pStrides, src.getData(), base);
201 if (std::is_const<Element>::value) {
202 result.attr("flags")["WRITEABLE"] = false;
203 }
204 return result.release();
205 }
206
207 bool isNone;
208 Wrapper wrapper;
209};
210
211} // namespace ndarray
212
213namespace pybind11 {
214namespace detail {
215
216/* @brief A pybind11 type_caster for ndarray::Array
217 */
218template <typename T, int N, int C>
219class type_caster< ndarray::Array<T,N,C> > {
221public:
222
223 bool load(handle src, bool) {
224 return _helper.init(src) && _helper.check();
225 }
226
227 void set_value() {
228 _value = _helper.convert();
229 }
230
231 static handle cast(const ndarray::Array<T,N,C> &src, return_value_policy /* policy */, handle /* parent */) {
232 return Helper::toPython(src);
233 }
234
235 static constexpr auto name = _("numpy.ndarray");
236
237 static handle cast(const ndarray::Array<T,N,C> *src, return_value_policy policy, handle parent) {
238 return cast(*src, policy, parent);
239 }
240
241 operator ndarray::Array<T,N,C> * () {
242 if (_helper.isNone) {
243 return nullptr;
244 } else {
245 set_value();
246 return &_value;
247 }
248 }
249
250 operator ndarray::Array<T,N,C> & () { set_value(); return _value; }
251
252 template <typename _T> using cast_op_type = pybind11::detail::cast_op_type<_T>;
253
254private:
256 Helper _helper;
257};
258
259} // namespace detail
260} // namespace pybind11
261
262#endif // !NDARRAY_pybind11_h_INCLUDED
Manager::Ptr getManager() const
Return the opaque object responsible for memory management.
Definition ArrayBase.h:136
Traits::Element Element
Data type of array elements.
Definition ArrayBase.h:50
Element * getData() const
Return a raw pointer to the first element of the array.
Definition ArrayBase.h:130
Strides getStrides() const
Return a Vector of the strides of all dimensions.
Definition ArrayBase.h:152
Index getShape() const
Return a Vector of the sizes of all dimensions.
Definition ArrayBase.h:149
A multidimensional strided array.
Definition Array.h:35
Functions that return an Eigen Map non-reference-counted view into an ndarray::Array.
detail::ExternalInitializer< T, N, Owner > external(T *data, Vector< U, N > const &shape, Vector< V, N > const &strides, Owner const &owner)
Create an expression that initializes an Array with externally allocated memory.
Definition initialization.h:176
Main public header file for ndarray.
Public header file for pybind11-based Python support.
Definition pybind11.h:83
A fixed-size 1D array class.
Definition Vector.h:82