@@ -381,6 +381,7 @@ struct type_caster<Eigen::Ref<T, Options, StrideType>,
381
381
// Extended version taking arbitrary strides
382
382
using DMap = Eigen::Map<const T, Options, DStride>;
383
383
using DMapCaster = make_caster<DMap>;
384
+ static constexpr bool DMapIsDifferent = !std::is_same_v<Map, DMap>;
384
385
385
386
/* *
386
387
* The constructor of ``Ref<const T>`` uses one of two strategies
@@ -391,14 +392,66 @@ struct type_caster<Eigen::Ref<T, Options, StrideType>,
391
392
*
392
393
* When the value below is ``true``, then it is guaranteed that
393
394
* ``Ref(<DMap instance>)`` owns the underlying data.
395
+ * Note that this ownership does *not* propagate to any further ``Ref``
396
+ * that might be constructed from it; the ``ToRef`` wrapper below
397
+ * attempts to avoid this hazard by ensuring there is no need to copy
398
+ * or move the initial ``Ref``.
394
399
*/
395
400
static constexpr bool DMapConstructorOwnsData =
396
401
!Eigen::internal::traits<Ref>::template match<DMap>::type::value;
397
402
403
+ /* *
404
+ * Result type of our cast operator, which can be used to construct a
405
+ * ``Ref`` from either ``Map`` or ``DMap``. Unfortunately, ``Ref`` does
406
+ * not model any sort of shared ownership; if we wind up creating a ``Ref``
407
+ * that owns the data, then copying it will produce a second ``Ref`` that
408
+ * refers to the original ``Ref``, which is likely to go out of scope
409
+ * soon. Eigen also has very minimal support for C++11 features such as
410
+ * moves, and in particular moving a ``Ref`` is equivalent to copying it
411
+ * in all released versions of Eigen as of this writing.
412
+ * This creates problems for nested type casters such as
413
+ * ``std::optional<Eigen::Ref<..>>``, which can't directly construct the
414
+ * cast operator's return value inside their data structure.
415
+ */
416
+ struct ToRef {
417
+ // DMap has maximally dynamic strides, so it won't contain
418
+ // fewer fields than Map (which might have some stride info
419
+ // known at compile time).
420
+ static_assert (alignof (DMap) >= alignof (Map) &&
421
+ sizeof (DMap) >= sizeof (Map));
422
+ alignas (DMap) char storage[sizeof (DMap)];
423
+ bool holds_dmap;
424
+ static constexpr bool MightHoldDMap = MaybeConvert && DMapIsDifferent;
425
+
426
+ ToRef (Map m) : holds_dmap(false ) { new (&storage) Map (m); }
427
+ template <bool Enable = MightHoldDMap, enable_if_t <Enable> = 0 >
428
+ ToRef (DMap m) : holds_dmap(true ) { new (&storage) DMap (m); }
429
+ ToRef (const ToRef&) = delete ;
430
+ ToRef& operator =(const ToRef&) = delete ;
431
+ ~ToRef () {
432
+ if constexpr (MightHoldDMap) {
433
+ if (holds_dmap) {
434
+ ((DMap *) storage)->~DMap ();
435
+ return ;
436
+ }
437
+ }
438
+ ((Map *) storage)->~Map ();
439
+ }
440
+
441
+ operator Ref () const {
442
+ if constexpr (MightHoldDMap) {
443
+ if (holds_dmap) {
444
+ return Ref (*(DMap *) storage);
445
+ }
446
+ }
447
+ return Ref (*(Map *) storage);
448
+ }
449
+ };
450
+
398
451
static constexpr auto Name =
399
452
const_name<MaybeConvert>(DMapCaster::Name, MapCaster::Name);
400
453
401
- template <typename T_> using Cast = Ref ;
454
+ template <typename T_> using Cast = ToRef ;
402
455
template <typename T_> static constexpr bool can_cast () { return true ; }
403
456
404
457
MapCaster caster;
@@ -459,13 +512,13 @@ struct type_caster<Eigen::Ref<T, Options, StrideType>,
459
512
cleanup);
460
513
}
461
514
462
- operator Ref () {
515
+ operator ToRef () {
463
516
if constexpr (MaybeConvert) {
464
517
if (dcaster.caster .value .is_valid ())
465
- return Ref (dcaster.operator DMap ());
518
+ return ToRef (dcaster.operator DMap ());
466
519
}
467
520
468
- return Ref (caster.operator Map ());
521
+ return ToRef (caster.operator Map ());
469
522
}
470
523
};
471
524
0 commit comments