@@ -113,9 +113,8 @@ 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
- }
116
+ template <typename Scalar>
117
+ using is_pyobject_dtype = std::is_base_of<npy_format_descriptor_object, npy_format_descriptor<Scalar>>;
119
118
120
119
// Helper struct for extracting information from an Eigen type
121
120
template <typename Type_> struct EigenProps {
@@ -149,9 +148,7 @@ template <typename Type_> struct EigenProps {
149
148
const auto dims = a.ndim ();
150
149
if (dims < 1 || dims > 2 )
151
150
return false ;
152
- bool is_pyobject = false ;
153
- if (is_pyobject_<Scalar>())
154
- is_pyobject = true ;
151
+ constexpr bool is_pyobject = is_pyobject_dtype<Scalar>::value;
155
152
ssize_t scalar_size = (is_pyobject ? static_cast <ssize_t >(sizeof (PyObject*)) :
156
153
static_cast <ssize_t >(sizeof (Scalar)));
157
154
if (dims == 2 ) { // Matrix type: require exact match (or dynamic)
@@ -233,17 +230,27 @@ template <typename props> handle eigen_array_cast(typename props::Type const &sr
233
230
src.data (), base);
234
231
}
235
232
else {
233
+ if (base) {
234
+ // Should be disabled by upstream calls to this method.
235
+ // TODO(eric.cousineau): Write tests to ensure that this is not
236
+ // reachable.
237
+ throw cast_error (
238
+ " dtype=object does not permit array referencing. "
239
+ " (NOTE: this generally not be reachable, as upstream APIs "
240
+ " should fail before this." );
241
+ }
242
+ handle empty_base{};
243
+ auto policy = return_value_policy::copy;
236
244
if (props::vector) {
237
245
a = array (
238
246
npy_format_descriptor<Scalar>::dtype (),
239
247
{ (size_t ) src.size () },
240
248
nullptr ,
241
- base
249
+ empty_base
242
250
);
243
- auto policy = base ? return_value_policy::automatic_reference : return_value_policy::copy;
244
251
for (ssize_t i = 0 ; i < src.size (); ++i) {
245
252
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 ));
253
+ auto value_ = reinterpret_steal<object>(make_caster<Scalar>::cast (src_val, policy, empty_base ));
247
254
if (!value_)
248
255
return handle ();
249
256
a.attr (" itemset" )(i, value_);
@@ -254,12 +261,11 @@ template <typename props> handle eigen_array_cast(typename props::Type const &sr
254
261
npy_format_descriptor<Scalar>::dtype (),
255
262
{(size_t ) src.rows (), (size_t ) src.cols ()},
256
263
nullptr ,
257
- base
264
+ empty_base
258
265
);
259
- auto policy = base ? return_value_policy::automatic_reference : return_value_policy::copy;
260
266
for (ssize_t i = 0 ; i < src.rows (); ++i) {
261
267
for (ssize_t j = 0 ; j < src.cols (); ++j) {
262
- auto value_ = reinterpret_steal<object>(make_caster<Scalar>::cast (src (i, j), policy, base ));
268
+ auto value_ = reinterpret_steal<object>(make_caster<Scalar>::cast (src (i, j), policy, empty_base ));
263
269
if (!value_)
264
270
return handle ();
265
271
a.attr (" itemset" )(i, j, value_);
@@ -323,7 +329,7 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
323
329
int result = 0 ;
324
330
// Allocate the new type, then build a numpy reference into it
325
331
value = Type (fits.rows , fits.cols );
326
- bool is_pyobject = is_pyobject_ <Scalar>() ;
332
+ constexpr bool is_pyobject = is_pyobject_dtype <Scalar>::value ;
327
333
328
334
if (!is_pyobject) {
329
335
auto ref = reinterpret_steal<array>(eigen_ref_array<props>(value));
@@ -374,22 +380,40 @@ struct type_caster<Type, enable_if_t<is_eigen_dense_plain<Type>::value>> {
374
380
// Cast implementation
375
381
template <typename CType>
376
382
static handle cast_impl (CType *src, return_value_policy policy, handle parent) {
377
- switch (policy) {
378
- case return_value_policy::take_ownership:
379
- case return_value_policy::automatic:
380
- return eigen_encapsulate<props>(src);
381
- case return_value_policy::move:
382
- return eigen_encapsulate<props>(new CType (std::move (*src)));
383
- case return_value_policy::copy:
384
- return eigen_array_cast<props>(*src);
385
- case return_value_policy::reference:
386
- case return_value_policy::automatic_reference:
387
- return eigen_ref_array<props>(*src);
388
- case return_value_policy::reference_internal:
389
- return eigen_ref_array<props>(*src, parent);
390
- default :
391
- throw cast_error (" unhandled return_value_policy: should not happen!" );
392
- };
383
+ constexpr bool is_pyobject = is_pyobject_dtype<Scalar>::value;
384
+ if (!is_pyobject) {
385
+ switch (policy) {
386
+ case return_value_policy::take_ownership:
387
+ case return_value_policy::automatic:
388
+ return eigen_encapsulate<props>(src);
389
+ case return_value_policy::move:
390
+ return eigen_encapsulate<props>(new CType (std::move (*src)));
391
+ case return_value_policy::copy:
392
+ return eigen_array_cast<props>(*src);
393
+ case return_value_policy::reference:
394
+ case return_value_policy::automatic_reference:
395
+ return eigen_ref_array<props>(*src);
396
+ case return_value_policy::reference_internal:
397
+ return eigen_ref_array<props>(*src, parent);
398
+ default :
399
+ throw cast_error (" unhandled return_value_policy: should not happen!" );
400
+ };
401
+ } else {
402
+ // For arrays of `dtype=object`, referencing is invalid, so we should squash that as soon as possible.
403
+ switch (policy) {
404
+ case return_value_policy::automatic:
405
+ case return_value_policy::move:
406
+ case return_value_policy::copy:
407
+ case return_value_policy::automatic_reference:
408
+ return eigen_array_cast<props>(*src);
409
+ case return_value_policy::take_ownership:
410
+ case return_value_policy::reference:
411
+ case return_value_policy::reference_internal:
412
+ throw cast_error (" dtype=object arrays must be copied, and cannot be referenced" );
413
+ default :
414
+ throw cast_error (" unhandled return_value_policy: should not happen!" );
415
+ };
416
+ }
393
417
}
394
418
395
419
public:
@@ -446,6 +470,7 @@ struct return_value_policy_override<Return, enable_if_t<is_eigen_dense_map<Retur
446
470
template <typename MapType> struct eigen_map_caster {
447
471
private:
448
472
using props = EigenProps<MapType>;
473
+ using Scalar = typename props::Scalar;
449
474
450
475
public:
451
476
@@ -456,18 +481,33 @@ template <typename MapType> struct eigen_map_caster {
456
481
// that this means you need to ensure you don't destroy the object in some other way (e.g. with
457
482
// an appropriate keep_alive, or with a reference to a statically allocated matrix).
458
483
static handle cast (const MapType &src, return_value_policy policy, handle parent) {
459
- switch (policy) {
460
- case return_value_policy::copy:
461
- return eigen_array_cast<props>(src);
462
- case return_value_policy::reference_internal:
463
- return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
464
- case return_value_policy::reference:
465
- case return_value_policy::automatic:
466
- case return_value_policy::automatic_reference:
467
- return eigen_array_cast<props>(src, none (), is_eigen_mutable_map<MapType>::value);
468
- default :
469
- // move, take_ownership don't make any sense for a ref/map:
470
- pybind11_fail (" Invalid return_value_policy for Eigen Map/Ref/Block type" );
484
+ if (!is_pyobject_dtype<Scalar>::value) {
485
+ switch (policy) {
486
+ case return_value_policy::copy:
487
+ return eigen_array_cast<props>(src);
488
+ case return_value_policy::reference_internal:
489
+ return eigen_array_cast<props>(src, parent, is_eigen_mutable_map<MapType>::value);
490
+ case return_value_policy::reference:
491
+ case return_value_policy::automatic:
492
+ case return_value_policy::automatic_reference:
493
+ return eigen_array_cast<props>(src, none (), is_eigen_mutable_map<MapType>::value);
494
+ default :
495
+ // move, take_ownership don't make any sense for a ref/map:
496
+ pybind11_fail (" Invalid return_value_policy for Eigen Map/Ref/Block type" );
497
+ }
498
+ } else {
499
+ switch (policy) {
500
+ case return_value_policy::copy:
501
+ return eigen_array_cast<props>(src);
502
+ case return_value_policy::reference_internal:
503
+ case return_value_policy::reference:
504
+ case return_value_policy::automatic:
505
+ case return_value_policy::automatic_reference:
506
+ throw cast_error (" dtype=object arrays must be copied, and cannot be referenced" );
507
+ default :
508
+ // move, take_ownership don't make any sense for a ref/map:
509
+ pybind11_fail (" Invalid return_value_policy for Eigen Map/Ref/Block type" );
510
+ }
471
511
}
472
512
}
473
513
@@ -519,9 +559,14 @@ struct type_caster<
519
559
bool need_copy = !isinstance<Array>(src);
520
560
521
561
EigenConformable<props::row_major> fits;
522
- bool is_pyobject = false ;
523
- if (is_pyobject_<Scalar>()) {
524
- is_pyobject = true ;
562
+ constexpr bool is_pyobject = is_pyobject_dtype<Scalar>::value;
563
+ // TODO(eric.cousineau): Make this compile-time once Drake does not use this in any code
564
+ // for scalar types.
565
+ // static_assert(!(is_pyobject && need_writeable), "dtype=object cannot provide writeable references");
566
+ if (is_pyobject && need_writeable) {
567
+ throw cast_error (" dtype=object cannot provide writeable references" );
568
+ }
569
+ if (is_pyobject) {
525
570
need_copy = true ;
526
571
}
527
572
if (!need_copy) {
0 commit comments