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