From 546f6fce1aa72e576bc334afa0a9000b743df7a8 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Fri, 20 Jan 2017 00:59:26 -0500 Subject: [PATCH] Add an ability to avoid forcing rvp::move Eigen::Ref objects, when returned, are almost always returned as rvalues; what's important is the data they reference, not the outer shell, and so we want to be able to use `::copy`, `::reference_internal`, etc. to refer to the data the Eigen::Ref references (in the following commits), rather than the Eigen::Ref instance itself. This moves the policy override into a struct so that code that wants to avoid it (or wants to provide some other Return-type-conditional override) can create a specialization of return_value_policy_override in order to override the override. This lets an Eigen::Ref-returning function be bound with `rvp::copy`, for example, to specify that the data should be copied into a new numpy array rather than referenced, or `rvp::reference_internal` to indicate that it should be referenced, but a keep-alive used (actually, we used the array's `base` rather than a py::keep_alive in such a case, but it accomplishes the same thing). --- include/pybind11/cast.h | 11 +++++++++++ include/pybind11/pybind11.h | 6 ++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 6cb02fd3a..b7128d184 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1097,6 +1097,17 @@ template using cast_is_temporary_value_reference = bool_constant !std::is_base_of>::value >; +// When a value returned from a C++ function is being cast back to Python, we almost always want to +// force `policy = move`, regardless of the return value policy the function/method was declared +// with. Some classes (most notably Eigen::Ref and related) need to avoid this, and so can do so by +// specializing this struct. +template struct return_value_policy_override { + static return_value_policy policy(return_value_policy p) { + return !std::is_lvalue_reference::value && !std::is_pointer::value + ? return_value_policy::move : p; + } +}; + // Basic python -> C++ casting; throws if casting fails template type_caster &load_type(type_caster &conv, const handle &handle) { if (!conv.load(handle, true)) { diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index ec3a5b0be..973143327 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -135,10 +135,8 @@ protected: ? &call.func.data : call.func.data[0]); capture *cap = const_cast(reinterpret_cast(data)); - /* Override policy for rvalues -- always move */ - constexpr auto is_rvalue = !std::is_pointer::value - && !std::is_lvalue_reference::value; - const auto policy = is_rvalue ? return_value_policy::move : call.func.policy; + /* Override policy for rvalues -- usually to enforce rvp::move on an rvalue */ + const auto policy = detail::return_value_policy_override::policy(call.func.policy); /* Perform the function call */ handle result = cast_out::cast(args_converter.template call(cap->f),