Skip to content

Commit dccf0e5

Browse files
committed
fix: Invert input/output context if a Python function is called from C++
When the Callable itself is an input (parameter) to a C++ function, its arguments are outputs (C++ passes them to the Python callback), and vice versa. Therefore, we must invert them if C++ calls a Python function, but keep them the same in the other direction.
1 parent 81817ae commit dccf0e5

3 files changed

Lines changed: 14 additions & 4 deletions

File tree

include/pybind11/detail/descr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,5 +222,10 @@ constexpr descr<N + 4, Ts...> return_descr(const descr<N, Ts...> &descr) {
222222
return const_name("@$") + descr + const_name("@!");
223223
}
224224

225+
template <size_t N, typename... Ts>
226+
constexpr descr<N + 4, Ts...> inv_descr(const descr<N, Ts...> &descr) {
227+
return const_name("@~") + descr + const_name("@!");
228+
}
229+
225230
PYBIND11_NAMESPACE_END(detail)
226231
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/functional.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,8 @@ struct type_caster<std::function<Return(Args...)>> {
138138
PYBIND11_TYPE_CASTER(
139139
type,
140140
const_name("collections.abc.Callable[[")
141-
+ ::pybind11::detail::concat(::pybind11::detail::arg_descr(make_caster<Args>::name)...)
142-
+ const_name("], ") + ::pybind11::detail::return_descr(make_caster<retval_type>::name)
143-
+ const_name("]"));
141+
+ ::pybind11::detail::concat(::pybind11::detail::inv_descr(make_caster<Args>::name)...)
142+
+ const_name("], ") + make_caster<retval_type>::name + const_name("]"));
144143
};
145144

146145
PYBIND11_NAMESPACE_END(detail)

include/pybind11/pybind11.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
185185
signature += *++pc;
186186
} else if (c == '@') {
187187
// `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see
188-
// typing::Callable/detail::arg_descr/detail::return_descr)
188+
// typing::Callable/detail::arg_descr/detail::return_descr).
189+
// `@~ ... @!` inverts the current context (see detail::inv_descr).
189190
if (*(pc + 1) == '^') {
190191
is_return_value.emplace(false);
191192
++pc;
@@ -196,6 +197,11 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
196197
++pc;
197198
continue;
198199
}
200+
if (*(pc + 1) == '~') {
201+
is_return_value.emplace(!is_return_value.top());
202+
++pc;
203+
continue;
204+
}
199205
if (*(pc + 1) == '!') {
200206
is_return_value.pop();
201207
++pc;

0 commit comments

Comments
 (0)