Skip to content

Commit

Permalink
Merge pull request #30022 from wangxf123456/merge-status-callback
Browse files Browse the repository at this point in the history
Git merge status-callback
  • Loading branch information
wangxf123456 authored Apr 4, 2023
2 parents 50bb0bf + 8f0e2a3 commit d1b83fd
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 37 deletions.
83 changes: 46 additions & 37 deletions include/pybind11/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,50 @@

PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
PYBIND11_NAMESPACE_BEGIN(detail)
PYBIND11_NAMESPACE_BEGIN(type_caster_std_function_specializations)

// ensure GIL is held during functor destruction
struct func_handle {
function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept
: f(std::move(f_)) {
}
func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};

// to emulate 'move initialization capture' in C++11
struct func_wrapper_base {
func_handle hfunc;
return_value_policy_pack rvpp;
func_wrapper_base(func_handle &&hf, const return_value_policy_pack &rvpp) noexcept
: hfunc(hf), rvpp(rvpp) {}
};

template <typename Return, typename... Args>
struct func_wrapper : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f.call_with_policies(rvpp, std::forward<Args>(args)...)
.template cast<Return>();
}
};

PYBIND11_NAMESPACE_END(type_caster_std_function_specializations)

template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...)>> {
Expand Down Expand Up @@ -77,43 +121,8 @@ struct type_caster<std::function<Return(Args...)>> {
// See PR #1413 for full details
}

// ensure GIL is held during functor destruction
struct func_handle {
function f;
#if !(defined(_MSC_VER) && _MSC_VER == 1916 && defined(PYBIND11_CPP17))
// This triggers a syntax error under very special conditions (very weird indeed).
explicit
#endif
func_handle(function &&f_) noexcept
: f(std::move(f_)) {
}
func_handle(const func_handle &f_) { operator=(f_); }
func_handle &operator=(const func_handle &f_) {
gil_scoped_acquire acq;
f = f_.f;
return *this;
}
~func_handle() {
gil_scoped_acquire acq;
function kill_f(std::move(f));
}
};

// to emulate 'move initialization capture' in C++11
struct func_wrapper {
func_handle hfunc;
return_value_policy_pack rvpp;
func_wrapper(func_handle &&hf, const return_value_policy_pack &rvpp) noexcept
: hfunc(std::move(hf)), rvpp(rvpp) {}
Return operator()(Args... args) const {
gil_scoped_acquire acq;
// casts the returned object as a rvalue to the return type
return hfunc.f.call_with_policies(rvpp, std::forward<Args>(args)...)
.template cast<Return>();
}
};

value = func_wrapper(func_handle(std::move(func)), fpp.rvpp);
value = type_caster_std_function_specializations::func_wrapper<Return, Args...>(
type_caster_std_function_specializations::func_handle(std::move(func)), fpp.rvpp);
return true;
}

Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ set(PYBIND11_TEST_FILES
test_thread
test_type_caster_odr_guard_1
test_type_caster_odr_guard_2
test_type_caster_std_function_specializations
test_union
test_virtual_functions)

Expand Down
46 changes: 46 additions & 0 deletions tests/test_type_caster_std_function_specializations.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>

#include "pybind11_tests.h"

namespace py = pybind11;

namespace {

struct SpecialReturn {
int value = 99;
};

} // namespace

namespace pybind11 {
namespace detail {
namespace type_caster_std_function_specializations {

template <typename... Args>
struct func_wrapper<SpecialReturn, Args...> : func_wrapper_base {
using func_wrapper_base::func_wrapper_base;
SpecialReturn operator()(Args... args) const {
gil_scoped_acquire acq;
SpecialReturn result;
try {
result = hfunc.f(std::forward<Args>(args)...).template cast<SpecialReturn>();
} catch (error_already_set &) {
result.value += 1;
}
result.value += 100;
return result;
}
};

} // namespace type_caster_std_function_specializations
} // namespace detail
} // namespace pybind11

TEST_SUBMODULE(type_caster_std_function_specializations, m) {
py::class_<SpecialReturn>(m, "SpecialReturn")
.def(py::init<>())
.def_readwrite("value", &SpecialReturn::value);
m.def("call_callback_with_special_return",
[](const std::function<SpecialReturn()> &func) { return func(); });
}
13 changes: 13 additions & 0 deletions tests/test_type_caster_std_function_specializations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from pybind11_tests import type_caster_std_function_specializations as m


def test_callback_with_special_return():
def return_special():
return m.SpecialReturn()

def raise_exception():
raise ValueError("called raise_exception.")

assert return_special().value == 99
assert m.call_callback_with_special_return(return_special).value == 199
assert m.call_callback_with_special_return(raise_exception).value == 200

0 comments on commit d1b83fd

Please sign in to comment.