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