@@ -107,6 +107,10 @@ struct eigen_extract_stride<Eigen::Map<PlainObjectType, MapOptions, StrideType>>
107
107
template <typename PlainObjectType, int Options, typename StrideType>
108
108
struct eigen_extract_stride <Eigen::Ref<PlainObjectType, Options, StrideType>> { using type = StrideType; };
109
109
110
+ template <typename Scalar> bool is_pyobject_ () {
111
+ return static_cast <pybind11::detail::npy_api::constants>(npy_format_descriptor<Scalar>::value) == npy_api::NPY_OBJECT_;
112
+ }
113
+
110
114
// Helper struct for extracting information from an Eigen type
111
115
template <typename Type_> struct EigenProps {
112
116
using Type = Type_;
@@ -139,14 +143,19 @@ template <typename Type_> struct EigenProps {
139
143
const auto dims = a.ndim ();
140
144
if (dims < 1 || dims > 2 )
141
145
return false ;
142
-
146
+ bool is_pyobject = false ;
147
+ if (is_pyobject_<Scalar>())
148
+ is_pyobject = true ;
149
+ ssize_t scalar_size = (is_pyobject ? static_cast <ssize_t >(sizeof (PyObject*)) :
150
+ static_cast <ssize_t >(sizeof (Scalar)));
143
151
if (dims == 2 ) { // Matrix type: require exact match (or dynamic)
144
152
145
153
EigenIndex
146
154
np_rows = a.shape (0 ),
147
155
np_cols = a.shape (1 ),
148
- np_rstride = a.strides (0 ) / static_cast <ssize_t >(sizeof (Scalar)),
149
- np_cstride = a.strides (1 ) / static_cast <ssize_t >(sizeof (Scalar));
156
+ np_rstride = a.strides (0 ) / scalar_size,
157
+ np_cstride = a.strides (1 ) / scalar_size;
158
+
150
159
if ((fixed_rows && np_rows != rows) || (fixed_cols && np_cols != cols))
151
160
return false ;
152
161
@@ -156,7 +165,7 @@ template <typename Type_> struct EigenProps {
156
165
// Otherwise we're storing an n-vector. Only one of the strides will be used, but whichever
157
166
// is used, we want the (single) numpy stride value.
158
167
const EigenIndex n = a.shape (0 ),
159
- stride = a.strides (0 ) / static_cast < ssize_t >( sizeof (Scalar)) ;
168
+ stride = a.strides (0 ) / scalar_size ;
160
169
161
170
if (vector) { // Eigen type is a compile-time vector
162
171
if (fixed && size != n)
@@ -207,11 +216,51 @@ template <typename Type_> struct EigenProps {
207
216
template <typename props> handle eigen_array_cast (typename props::Type const &src, handle base = handle(), bool writeable = true) {
208
217
constexpr ssize_t elem_size = sizeof (typename props::Scalar);
209
218
array a;
210
- if (props::vector)
211
- a = array ({ src.size () }, { elem_size * src.innerStride () }, src.data (), base);
212
- else
213
- a = array ({ src.rows (), src.cols () }, { elem_size * src.rowStride (), elem_size * src.colStride () },
214
- src.data (), base);
219
+ using Scalar = typename props::Type::Scalar;
220
+ bool is_pyoject = static_cast <pybind11::detail::npy_api::constants>(npy_format_descriptor<Scalar>::value) == npy_api::NPY_OBJECT_;
221
+
222
+ if (!is_pyoject) {
223
+ if (props::vector)
224
+ a = array ({ src.size () }, { elem_size * src.innerStride () }, src.data (), base);
225
+ else
226
+ a = array ({ src.rows (), src.cols () }, { elem_size * src.rowStride (), elem_size * src.colStride () },
227
+ src.data (), base);
228
+ }
229
+ else {
230
+ if (props::vector) {
231
+ a = array (
232
+ npy_format_descriptor<Scalar>::dtype (),
233
+ { (size_t ) src.size () },
234
+ nullptr ,
235
+ base
236
+ );
237
+ auto policy = base ? return_value_policy::automatic_reference : return_value_policy::copy;
238
+ for (ssize_t i = 0 ; i < src.size (); ++i) {
239
+ const Scalar src_val = props::fixed_rows ? src (0 , i) : src (i, 0 );
240
+ auto value_ = reinterpret_steal<object>(make_caster<Scalar>::cast (src_val, policy, base));
241
+ if (!value_)
242
+ return handle ();
243
+ a.attr (" itemset" )(i, value_);
244
+ }
245
+ }
246
+ else {
247
+ a = array (
248
+ npy_format_descriptor<Scalar>::dtype (),
249
+ {(size_t ) src.rows (), (size_t ) src.cols ()},
250
+ nullptr ,
251
+ base
252
+ );
253
+ auto policy = base ? return_value_policy::automatic_reference : return_value_policy::copy;
254
+ for (ssize_t i = 0 ; i < src.rows (); ++i) {
255
+ for (ssize_t j = 0 ; j < src.cols (); ++j) {
256
+ auto value_ = reinterpret_steal<object>(make_caster<Scalar>::cast (src (i, j), policy, base));
257
+ if (!value_)
258
+ return handle ();
259
+ a.attr (" itemset" )(i, j, value_);
260
+ }
261
+ }
262
+ }
263
+ }
215
264
216
265
if (!writeable)
217
266
array_proxy (a.ptr ())->flags &= ~detail::npy_api::NPY_ARRAY_WRITEABLE_;
@@ -265,14 +314,46 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
265
314
auto fits = props::conformable (buf);
266
315
if (!fits)
267
316
return false ;
268
-
317
+ int result = 0 ;
269
318
// Allocate the new type, then build a numpy reference into it
270
319
value = Type (fits.rows , fits.cols );
271
- auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
272
- if (dims == 1 ) ref = ref.squeeze ();
273
- else if (ref.ndim () == 1 ) buf = buf.squeeze ();
274
-
275
- int result = detail::npy_api::get ().PyArray_CopyInto_ (ref.ptr (), buf.ptr ());
320
+ bool is_pyobject = is_pyobject_<Scalar>();
321
+
322
+ if (!is_pyobject) {
323
+ auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
324
+ if (dims == 1 ) ref = ref.squeeze ();
325
+ else if (ref.ndim () == 1 ) buf = buf.squeeze ();
326
+ result =
327
+ detail::npy_api::get ().PyArray_CopyInto_ (ref.ptr (), buf.ptr ());
328
+ }
329
+ else {
330
+ if (dims == 1 ) {
331
+ if (Type::RowsAtCompileTime == Eigen::Dynamic)
332
+ value.resize (buf.shape (0 ), 1 );
333
+ if (Type::ColsAtCompileTime == Eigen::Dynamic)
334
+ value.resize (1 , buf.shape (0 ));
335
+
336
+ for (ssize_t i = 0 ; i < buf.shape (0 ); ++i) {
337
+ make_caster <Scalar> conv_val;
338
+ if (!conv_val.load (buf.attr (" item" )(i).cast <pybind11::object>(), convert))
339
+ return false ;
340
+ value (i) = cast_op<Scalar>(conv_val);
341
+ }
342
+ } else {
343
+ if (Type::RowsAtCompileTime == Eigen::Dynamic || Type::ColsAtCompileTime == Eigen::Dynamic) {
344
+ value.resize (buf.shape (0 ), buf.shape (1 ));
345
+ }
346
+ for (ssize_t i = 0 ; i < buf.shape (0 ); ++i) {
347
+ for (ssize_t j = 0 ; j < buf.shape (1 ); ++j) {
348
+ // p is the const void pointer to the item
349
+ make_caster<Scalar> conv_val;
350
+ if (!conv_val.load (buf.attr (" item" )(i,j).cast <pybind11::object>(), convert))
351
+ return false ;
352
+ value (i,j) = cast_op<Scalar>(conv_val);
353
+ }
354
+ }
355
+ }
356
+ }
276
357
277
358
if (result < 0 ) { // Copy failed!
278
359
PyErr_Clear ();
@@ -424,13 +505,19 @@ struct type_caster<
424
505
// storage order conversion. (Note that we refuse to use this temporary copy when loading an
425
506
// argument for a Ref<M> with M non-const, i.e. a read-write reference).
426
507
Array copy_or_ref;
508
+ typename std::remove_cv<PlainObjectType>::type val;
427
509
public:
428
510
bool load (handle src, bool convert) {
429
511
// First check whether what we have is already an array of the right type. If not, we can't
430
512
// avoid a copy (because the copy is also going to do type conversion).
431
513
bool need_copy = !isinstance<Array>(src);
432
514
433
515
EigenConformable<props::row_major> fits;
516
+ bool is_pyobject = false ;
517
+ if (is_pyobject_<Scalar>()) {
518
+ is_pyobject = true ;
519
+ need_copy = true ;
520
+ }
434
521
if (!need_copy) {
435
522
// We don't need a converting copy, but we also need to check whether the strides are
436
523
// compatible with the Ref's stride requirements
@@ -453,15 +540,53 @@ struct type_caster<
453
540
// We need to copy: If we need a mutable reference, or we're not supposed to convert
454
541
// (either because we're in the no-convert overload pass, or because we're explicitly
455
542
// instructed not to copy (via `py::arg().noconvert()`) we have to fail loading.
456
- if (!convert || need_writeable) return false ;
543
+ if (!is_pyobject && (!convert || need_writeable)) {
544
+ return false ;
545
+ }
457
546
458
547
Array copy = Array::ensure (src);
459
548
if (!copy) return false ;
460
549
fits = props::conformable (copy);
461
- if (!fits || !fits.template stride_compatible <props>())
550
+ if (!fits || !fits.template stride_compatible <props>()) {
462
551
return false ;
463
- copy_or_ref = std::move (copy);
464
- loader_life_support::add_patient (copy_or_ref);
552
+ }
553
+
554
+ if (!is_pyobject) {
555
+ copy_or_ref = std::move (copy);
556
+ loader_life_support::add_patient (copy_or_ref);
557
+ }
558
+ else {
559
+ auto dims = copy.ndim ();
560
+ if (dims == 1 ) {
561
+ if (Type::RowsAtCompileTime == Eigen::Dynamic || Type::ColsAtCompileTime == Eigen::Dynamic) {
562
+ val.resize (copy.shape (0 ), 1 );
563
+ }
564
+ for (ssize_t i = 0 ; i < copy.shape (0 ); ++i) {
565
+ make_caster <Scalar> conv_val;
566
+ if (!conv_val.load (copy.attr (" item" )(i).template cast <pybind11::object>(),
567
+ convert))
568
+ return false ;
569
+ val (i) = cast_op<Scalar>(conv_val);
570
+
571
+ }
572
+ } else {
573
+ if (Type::RowsAtCompileTime == Eigen::Dynamic || Type::ColsAtCompileTime == Eigen::Dynamic) {
574
+ val.resize (copy.shape (0 ), copy.shape (1 ));
575
+ }
576
+ for (ssize_t i = 0 ; i < copy.shape (0 ); ++i) {
577
+ for (ssize_t j = 0 ; j < copy.shape (1 ); ++j) {
578
+ // p is the const void pointer to the item
579
+ make_caster <Scalar> conv_val;
580
+ if (!conv_val.load (copy.attr (" item" )(i, j).template cast <pybind11::object>(),
581
+ convert))
582
+ return false ;
583
+ val (i, j) = cast_op<Scalar>(conv_val);
584
+ }
585
+ }
586
+ }
587
+ ref.reset (new Type (val));
588
+ return true ;
589
+ }
465
590
}
466
591
467
592
ref.reset ();
0 commit comments