eigenpy 3.12.0
Bindings between Numpy and Eigen using Boost.Python
Loading...
Searching...
No Matches
user-type.hpp
1//
2// Copyright (c) 2020-2022 INRIA
3//
4
5#ifndef __eigenpy_user_type_hpp__
6#define __eigenpy_user_type_hpp__
7
8#include <iostream>
9
10#include "eigenpy/fwd.hpp"
11#include "eigenpy/numpy-type.hpp"
12#include "eigenpy/register.hpp"
13
14namespace eigenpy {
17template <typename From, typename To>
18struct cast {
19 static To run(const From& from) {
20#pragma GCC diagnostic push
21#pragma GCC diagnostic ignored "-Wconversion"
22#pragma GCC diagnostic ignored "-Wfloat-conversion"
23 return static_cast<To>(from);
24#pragma GCC diagnostic pop
25 }
26};
27
28namespace internal {
29
30template <typename From, typename To>
31static void cast(void* from_, void* to_, npy_intp n, void* /*fromarr*/,
32 void* /*toarr*/) {
33 // std::cout << "cast::run" << std::endl;
34 const From* from = static_cast<From*>(from_);
35 To* to = static_cast<To*>(to_);
36 for (npy_intp i = 0; i < n; i++) {
37 to[i] = eigenpy::cast<From, To>::run(from[i]);
38 }
39}
40
41template <typename T>
42struct getitem {
53 static PyObject* run(void* data, void* /* arr */) {
54 // std::cout << "getitem" << std::endl;
55 T* elt_ptr = static_cast<T*>(data);
56 bp::object m(boost::ref(*elt_ptr));
57 Py_INCREF(m.ptr());
58 return m.ptr();
59 }
60};
61
62template <typename T, int type_code = NumpyEquivalentType<T>::type_code>
63struct SpecialMethods {
64 inline static void copyswap(void* /*dst*/, void* /*src*/, int /*swap*/,
65 void* /*arr*/) /*{}*/;
66 inline static PyObject* getitem(void* /*ip*/,
67 void* /*ap*/) /*{ return NULL; }*/;
68 inline static int setitem(PyObject* /*op*/, void* /*ov*/,
69 void* /*ap*/) /*{ return -1; }*/;
70 inline static void copyswapn(void* /*dest*/, long /*dstride*/, void* /*src*/,
71 long /*sstride*/, long /*n*/, int /*swap*/,
72 void* /*arr*/) /*{}*/;
73 inline static npy_bool nonzero(
74 void* /*ip*/, void* /*array*/) /*{ return (npy_bool)false; }*/;
75 inline static void dotfunc(void* /*ip0_*/, npy_intp /*is0*/, void* /*ip1_*/,
76 npy_intp /*is1*/, void* /*op*/, npy_intp /*n*/,
77 void* /*arr*/);
78 inline static int fill(void* data_, npy_intp length, void* arr);
79 inline static int fillwithscalar(void* buffer_, npy_intp length, void* value,
80 void* arr);
81};
82
83template <typename T>
84struct OffsetOf {
85 struct Data {
86 char c;
87 T v;
88 };
89
90 enum { value = offsetof(Data, v) };
91};
92
93template <typename T>
94struct SpecialMethods<T, NPY_USERDEF> {
95 static void copyswap(void* dst, void* src, int swap, void* /*arr*/) {
96 // std::cout << "copyswap" << std::endl;
97 if (src != NULL) {
98 T& t1 = *static_cast<T*>(dst);
99 T& t2 = *static_cast<T*>(src);
100 t1 = t2;
101 }
102
103 if (swap) {
104 T& t1 = *static_cast<T*>(dst);
105 T& t2 = *static_cast<T*>(src);
106 std::swap(t1, t2);
107 }
108 }
109
110 static PyObject* getitem(void* ip, void* ap) {
111 return eigenpy::internal::getitem<T>::run(ip, ap);
112 }
113
120
128
129 inline static int setitem(PyObject* src_obj, void* dest_ptr, void* array) {
130 // std::cout << "setitem" << std::endl;
131 if (array == NULL) {
132 eigenpy::Exception("Cannot retrieve the type stored in the array.");
133 return -1;
134 }
135
136 PyArrayObject* py_array = static_cast<PyArrayObject*>(array);
137 PyArray_Descr* descr = PyArray_DTYPE(py_array);
138 PyTypeObject* array_scalar_type = descr->typeobj;
139 PyTypeObject* src_obj_type = Py_TYPE(src_obj);
140
141 T& dest = *static_cast<T*>(dest_ptr);
142 if (array_scalar_type != src_obj_type) {
143 long long src_value = PyLong_AsLongLong(src_obj);
144 if (src_value == -1 && PyErr_Occurred()) {
145 std::stringstream ss;
146 ss << "The input type is of wrong type. ";
147 ss << "The expected type is " << bp::type_info(typeid(T)).name()
148 << std::endl;
149 eigenpy::Exception(ss.str());
150 return -1;
151 }
152
153 dest = T(src_value);
154
155 } else {
156 bp::extract<T&> extract_src_obj(src_obj);
157 if (!extract_src_obj.check()) {
158 std::cout << "if (!extract_src_obj.check())" << std::endl;
159 std::stringstream ss;
160 ss << "The input type is of wrong type. ";
161 ss << "The expected type is " << bp::type_info(typeid(T)).name()
162 << std::endl;
163 eigenpy::Exception(ss.str());
164 return -1;
165 }
166
167 const T& src = extract_src_obj();
168 T& dest = *static_cast<T*>(dest_ptr);
169 dest = src;
170 }
171
172 return 0;
173 }
174
175 inline static void copyswapn(void* dst, long dstride, void* src, long sstride,
176 long n, int swap, void* array) {
177 // std::cout << "copyswapn" << std::endl;
178
179 char* dstptr = static_cast<char*>(dst);
180 char* srcptr = static_cast<char*>(src);
181
182 PyArrayObject* py_array = static_cast<PyArrayObject*>(array);
183 PyArray_CopySwapFunc* copyswap =
184 PyDataType_GetArrFuncs(PyArray_DESCR(py_array))->copyswap;
185
186 for (npy_intp i = 0; i < n; i++) {
187 copyswap(dstptr, srcptr, swap, array);
188 dstptr += dstride;
189 srcptr += sstride;
190 }
191 }
192
193 inline static npy_bool nonzero(void* ip, void* array) {
194 // std::cout << "nonzero" << std::endl;
195 static const T ZeroValue = T(0);
196 PyArrayObject* py_array = static_cast<PyArrayObject*>(array);
197 if (py_array == NULL || PyArray_ISBEHAVED_RO(py_array)) {
198 const T& value = *static_cast<T*>(ip);
199 return (npy_bool)(value != ZeroValue);
200 } else {
201 T tmp_value;
202 PyDataType_GetArrFuncs(PyArray_DESCR(py_array))
203 ->copyswap(&tmp_value, ip, PyArray_ISBYTESWAPPED(py_array), array);
204 return (npy_bool)(tmp_value != ZeroValue);
205 }
206 }
207
208 inline static void dotfunc(void* ip0_, npy_intp is0, void* ip1_, npy_intp is1,
209 void* op, npy_intp n, void* /*arr*/) {
210 // std::cout << "dotfunc" << std::endl;
211 typedef Eigen::Matrix<T, Eigen::Dynamic, 1> VectorT;
212 typedef Eigen::InnerStride<Eigen::Dynamic> InputStride;
213 typedef const Eigen::Map<const VectorT, 0, InputStride> ConstMapType;
214
215 ConstMapType v0(static_cast<T*>(ip0_), n,
216 InputStride(is0 / (Eigen::DenseIndex)sizeof(T))),
217 v1(static_cast<T*>(ip1_), n,
218 InputStride(is1 / (Eigen::DenseIndex)sizeof(T)));
219
220 *static_cast<T*>(op) = v0.dot(v1);
221 }
222
223 inline static int fillwithscalar(void* buffer_, npy_intp length, void* value,
224 void* /*arr*/) {
225 // std::cout << "fillwithscalar" << std::endl;
226 T r = *static_cast<T*>(value);
227 T* buffer = static_cast<T*>(buffer_);
228 npy_intp i;
229 for (i = 0; i < length; i++) {
230 buffer[i] = r;
231 }
232 return 0;
233 }
234
235 static int fill(void* data_, npy_intp length, void* /*arr*/) {
236 // std::cout << "fill" << std::endl;
237 T* data = static_cast<T*>(data_);
238 const T delta = data[1] - data[0];
239 T r = data[1];
240 npy_intp i;
241 for (i = 2; i < length; i++) {
242 r = r + delta;
243 data[i] = r;
244 }
245 return 0;
246 }
247
248}; // struct SpecialMethods<T,NPY_USERDEF>
249
250} // namespace internal
251
252template <typename From, typename To>
253bool registerCast(const bool safe) {
254 PyArray_Descr* from_array_descr = Register::getPyArrayDescr<From>();
255 // int from_typenum = Register::getTypeCode<From>();
256
257 // PyTypeObject * to_py_type = Register::getPyType<To>();
258 int to_typenum = Register::getTypeCode<To>();
259 assert(to_typenum >= 0 && "to_typenum is not valid");
260 assert(from_array_descr != NULL && "from_array_descr is not valid");
261
262 // std::cout << "From: " << bp::type_info(typeid(From)).name() << " " <<
263 // Register::getTypeCode<From>()
264 // << " to: " << bp::type_info(typeid(To)).name() << " " <<
265 // Register::getTypeCode<To>()
266 // << "\n to_typenum: " << to_typenum
267 // << std::endl;
268
269 if (call_PyArray_RegisterCastFunc(from_array_descr, to_typenum,
270 static_cast<PyArray_VectorUnaryFunc*>(
271 &eigenpy::internal::cast<From, To>)) <
272 0) {
273 std::stringstream ss;
274 ss << "PyArray_RegisterCastFunc of the cast from "
275 << bp::type_info(typeid(From)).name() << " to "
276 << bp::type_info(typeid(To)).name() << " has failed.";
277 eigenpy::Exception(ss.str());
278 return false;
279 }
280
281 if (safe && call_PyArray_RegisterCanCast(from_array_descr, to_typenum,
282 NPY_NOSCALAR) < 0) {
283 std::stringstream ss;
284 ss << "PyArray_RegisterCanCast of the cast from "
285 << bp::type_info(typeid(From)).name() << " to "
286 << bp::type_info(typeid(To)).name() << " has failed.";
287 eigenpy::Exception(ss.str());
288 return false;
289 }
290
291 return true;
292}
293
296template <typename T>
297boost::python::object getInstanceClass() {
298 // Query into the registry for type T.
299 bp::type_info type = bp::type_id<T>();
300 const bp::converter::registration* registration =
301 bp::converter::registry::query(type);
302
303 // If the class is not registered, return None.
304 if (!registration) {
305 // std::cerr<<"Class Not Registered. Returning Empty."<<std::endl;
306 return bp::object();
307 }
308
309 bp::handle<PyTypeObject> handle(
310 bp::borrowed(registration->get_class_object()));
311 return bp::object(handle);
312}
313
314template <typename Scalar>
315int registerNewType(PyTypeObject* py_type_ptr = NULL) {
316 // Check whether the type is a Numpy native type.
317 // In this case, the registration is not required.
318 if (isNumpyNativeType<Scalar>())
319 return NumpyEquivalentType<Scalar>::type_code;
320
321 // Retrieve the registered type for the current Scalar
322 if (py_type_ptr == NULL) { // retrive the type from Boost.Python
323 py_type_ptr = Register::getPyType<Scalar>();
324 }
325
326 if (Register::isRegistered(py_type_ptr))
327 return Register::getTypeCode(
328 py_type_ptr); // the type is already registered
329
330 PyArray_GetItemFunc* getitem = &internal::SpecialMethods<Scalar>::getitem;
331 PyArray_SetItemFunc* setitem = &internal::SpecialMethods<Scalar>::setitem;
332 PyArray_NonzeroFunc* nonzero = &internal::SpecialMethods<Scalar>::nonzero;
333 PyArray_CopySwapFunc* copyswap = &internal::SpecialMethods<Scalar>::copyswap;
334 PyArray_CopySwapNFunc* copyswapn = reinterpret_cast<PyArray_CopySwapNFunc*>(
335 &internal::SpecialMethods<Scalar>::copyswapn);
336 PyArray_DotFunc* dotfunc = &internal::SpecialMethods<Scalar>::dotfunc;
337 PyArray_FillFunc* fill = &internal::SpecialMethods<Scalar>::fill;
338 PyArray_FillWithScalarFunc* fillwithscalar =
339 &internal::SpecialMethods<Scalar>::fillwithscalar;
340
341 int code = Register::registerNewType(
342 py_type_ptr, &typeid(Scalar), sizeof(Scalar),
343 internal::OffsetOf<Scalar>::value, getitem, setitem, nonzero, copyswap,
344 copyswapn, dotfunc, fill, fillwithscalar);
345
346 call_PyArray_RegisterCanCast(call_PyArray_DescrFromType(NPY_OBJECT), code,
347 NPY_NOSCALAR);
348
349 return code;
350}
351
352} // namespace eigenpy
353
354#endif // __eigenpy_user_type_hpp__
boost::python::object getInstanceClass()
Get the class object for a wrapped type that has been exposed through Boost.Python.
Default cast algo to cast a From to To. Can be specialized for any types.
Definition user-type.hpp:18