diff --git a/include/nanobind/eigen/dense.h b/include/nanobind/eigen/dense.h index 60486ce51..b61abfae4 100644 --- a/include/nanobind/eigen/dense.h +++ b/include/nanobind/eigen/dense.h @@ -198,7 +198,7 @@ struct type_caster && owner = capsule(temp, [](void *p) noexcept { delete (T *) p; }); ptr = temp->data(); policy = rv_policy::reference; - } else if (policy == rv_policy::reference_internal) { + } else if (policy == rv_policy::reference_internal && cleanup->self()) { owner = borrow(cleanup->self()); policy = rv_policy::reference; } @@ -417,15 +417,17 @@ struct type_caster, if constexpr (MaybeConvert) { /* Generating an implicit copy requires some object to assume ownership. During a function call, ``dcaster`` can serve that - role (this case is detected by checking whether ``cleanup`` is - defined). When used in other situatons (e.g. ``nb::cast()``), - the created ``Eigen::Ref<..>`` must take ownership of the copy. - This is only guranteed to work if DMapConstructorOwnsData. + role (this case is detected by checking whether ``flags`` has + the ``manual`` flag set). When used in other situations (e.g. + ``nb::cast()``), the created ``Eigen::Ref<..>`` must take + ownership of the copy. This is only guranteed to work if + DMapConstructorOwnsData. If neither of these is possible, we disable implicit conversions. */ - if (!cleanup && !DMapConstructorOwnsData) + if ((flags & (uint8_t) cast_flags::manual) && + !DMapConstructorOwnsData) flags &= ~(uint8_t) cast_flags::convert; if (dcaster.from_python_(src, flags, cleanup)) diff --git a/include/nanobind/nb_cast.h b/include/nanobind/nb_cast.h index 817582458..d592bd08c 100644 --- a/include/nanobind/nb_cast.h +++ b/include/nanobind/nb_cast.h @@ -40,6 +40,9 @@ enum cast_flags : uint8_t { // Don't accept 'None' Python objects in the base class caster none_disallowed = (1 << 2), + + // Indicates that this cast is performed by nb::cast or nb::try_cast + manual = (1 << 3) }; /** @@ -411,53 +414,86 @@ template struct type_caster_base : type_caster_base_tag { template struct type_caster : type_caster_base { }; -NAMESPACE_END(detail) +template +T cast_impl(handle h) { + using Caster = detail::make_caster; -template -bool try_cast(const detail::api &value, T &out, bool convert = true) noexcept { + static_assert( + !(std::is_reference_v || std::is_pointer_v) || + detail::is_base_caster_v || + std::is_same_v, + "nanobind::cast(): cannot return a reference to a temporary."); + + Caster caster; + bool rv; + if constexpr (Convert) { + cleanup_list cleanup(nullptr); + rv = caster.from_python(h.ptr(), + ((uint8_t) cast_flags::convert) | + ((uint8_t) cast_flags::manual), + &cleanup); + cleanup.release(); // 'from_python' is 'noexcept', so this always runs + } else { + rv = caster.from_python(h.ptr(), (uint8_t) cast_flags::manual, nullptr); + } + + if (!rv) + detail::raise_cast_error(); + return caster.operator detail::cast_t(); +} + +template +bool try_cast_impl(handle h, T &out) noexcept { using Caster = detail::make_caster; static_assert(!std::is_same_v, "nanobind::try_cast(): cannot return a reference to a temporary."); Caster caster; - if (caster.from_python(value.derived().ptr(), - convert ? (uint8_t) detail::cast_flags::convert - : (uint8_t) 0, nullptr)) { + bool rv; + if constexpr (Convert) { + cleanup_list cleanup(nullptr); + rv = caster.from_python(h.ptr(), + ((uint8_t) cast_flags::convert) | + ((uint8_t) cast_flags::manual), + &cleanup); + cleanup.release(); // 'from_python' is 'noexcept', so this always runs + } else { + rv = caster.from_python(h.ptr(), (uint8_t) cast_flags::manual, nullptr); + } + + if (rv) { try { out = caster.operator detail::cast_t(); return true; - } catch (const builtin_exception&) { - return false; - } + } catch (const builtin_exception&) { } } return false; } +NAMESPACE_END(detail) + template -T cast(const detail::api &value, bool convert = true) { +NB_INLINE T cast(const detail::api &value, bool convert = true) { if constexpr (std::is_same_v) { return; } else { - using Caster = detail::make_caster; - - static_assert( - !(std::is_reference_v || std::is_pointer_v) || - detail::is_base_caster_v || - std::is_same_v, - "nanobind::cast(): cannot return a reference to a temporary."); - - Caster caster; - if (!caster.from_python(value.derived().ptr(), - convert ? (uint8_t) detail::cast_flags::convert - : (uint8_t) 0, nullptr)) - detail::raise_cast_error(); - - return caster.operator detail::cast_t(); + if (convert) + return detail::cast_impl(value); + else + return detail::cast_impl(value); } } +template +NB_INLINE bool try_cast(const detail::api &value, T &out, bool convert = true) noexcept { + if (convert) + return detail::try_cast_impl(value, out); + else + return detail::try_cast_impl(value, out); +} + template object cast(T &&value, rv_policy policy = rv_policy::automatic_reference) { handle h = detail::make_caster::from_cpp((detail::forward_t) value,