mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-11 08:03:55 +00:00
Support binding noexcept function/methods in C++17
When compiling in C++17 mode the noexcept specifier is part of the function type. This causes a failure in pybind11 because, by omitting a noexcept specifier when deducing function return and argument types, we are implicitly making `noexcept(false)` part of the type. This means that functions with `noexcept` fail to match the function templates in cpp_function (and other places), and we get compilation failure (we end up trying to fit it into the lambda function version, which fails since a function pointer has no `operator()`). We can, however, deduce the true/false `B` in noexcept(B), so we don't need to add a whole other set of overloads, but need to deduce the extra argument when under C++17. That will *not* work under pre-C++17, however. This commit adds two macros to fix the problem: under C++17 (with the appropriate feature macro set) they provide an extra `bool NoExceptions` template argument and provide the `noexcept(NoExceptions)` deduced specifier. Under pre-C++17 they expand to nothing. This is needed to compile pybind11 with gcc7 under -std=c++17.
This commit is contained in:
parent
79de508ef4
commit
6e036e78a7
@ -179,6 +179,17 @@ extern "C" {
|
||||
} \
|
||||
PyObject *pybind11_init()
|
||||
|
||||
// Function return value and argument type deduction support. When compiling under C++17 these
|
||||
// differ as C++17 makes the noexcept specifier part of the function type, while it is not part of
|
||||
// the type under earlier standards.
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
# define PYBIND11_NOEXCEPT_TPL_ARG , bool NoExceptions
|
||||
# define PYBIND11_NOEXCEPT_SPECIFIER noexcept(NoExceptions)
|
||||
#else
|
||||
# define PYBIND11_NOEXCEPT_TPL_ARG
|
||||
# define PYBIND11_NOEXCEPT_SPECIFIER
|
||||
#endif
|
||||
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
|
||||
using ssize_t = Py_ssize_t;
|
||||
@ -564,16 +575,16 @@ struct nodelete { template <typename T> void operator()(T*) { } };
|
||||
NAMESPACE_BEGIN(detail)
|
||||
template <typename... Args>
|
||||
struct overload_cast_impl {
|
||||
template <typename Return>
|
||||
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
|
||||
template <typename Return /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
constexpr auto operator()(Return (*pf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER) const noexcept
|
||||
-> decltype(pf) { return pf; }
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
|
||||
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, std::false_type = {}) const noexcept
|
||||
-> decltype(pmf) { return pmf; }
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept
|
||||
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
constexpr auto operator()(Return (Class::*pmf)(Args...) const PYBIND11_NOEXCEPT_SPECIFIER, std::true_type) const noexcept
|
||||
-> decltype(pmf) { return pmf; }
|
||||
};
|
||||
NAMESPACE_END(detail)
|
||||
|
@ -15,9 +15,12 @@
|
||||
NAMESPACE_BEGIN(pybind11)
|
||||
NAMESPACE_BEGIN(detail)
|
||||
|
||||
template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> {
|
||||
typedef std::function<Return(Args...)> type;
|
||||
typedef typename std::conditional<std::is_same<Return, void>::value, void_type, Return>::type retval_type;
|
||||
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
struct type_caster<std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>> {
|
||||
using type = std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>;
|
||||
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
|
||||
using function_type = Return (*) (Args...) PYBIND11_NOEXCEPT_SPECIFIER;
|
||||
|
||||
public:
|
||||
bool load(handle src_, bool) {
|
||||
if (src_.is_none())
|
||||
@ -38,10 +41,9 @@ public:
|
||||
if (PyCFunction_Check(src_.ptr())) {
|
||||
auto c = reinterpret_borrow<capsule>(PyCFunction_GetSelf(src_.ptr()));
|
||||
auto rec = (function_record *) c;
|
||||
using FunctionType = Return (*) (Args...);
|
||||
|
||||
if (rec && rec->is_stateless && rec->data[1] == &typeid(FunctionType)) {
|
||||
struct capture { FunctionType f; };
|
||||
if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) {
|
||||
struct capture { function_type f; };
|
||||
value = ((capture *) &rec->data)->f;
|
||||
return true;
|
||||
}
|
||||
@ -62,7 +64,7 @@ public:
|
||||
if (!f_)
|
||||
return none().inc_ref();
|
||||
|
||||
auto result = f_.template target<Return (*)(Args...)>();
|
||||
auto result = f_.template target<function_type>();
|
||||
if (result)
|
||||
return cpp_function(*result, policy).release();
|
||||
else
|
||||
|
@ -1145,13 +1145,15 @@ template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
|
||||
|
||||
NAMESPACE_END(detail)
|
||||
|
||||
template <typename Func, typename Return, typename... Args>
|
||||
detail::vectorize_helper<Func, Return, Args...> vectorize(const Func &f, Return (*) (Args ...)) {
|
||||
template <typename Func, typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
detail::vectorize_helper<Func, Return, Args...>
|
||||
vectorize(const Func &f, Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
|
||||
return detail::vectorize_helper<Func, Return, Args...>(f);
|
||||
}
|
||||
|
||||
template <typename Return, typename... Args>
|
||||
detail::vectorize_helper<Return (*) (Args ...), Return, Args...> vectorize(Return (*f) (Args ...)) {
|
||||
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
detail::vectorize_helper<Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER, Return, Args...>
|
||||
vectorize(Return (*f) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
|
||||
return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
|
||||
}
|
||||
|
||||
|
@ -44,8 +44,8 @@ public:
|
||||
cpp_function() { }
|
||||
|
||||
/// Construct a cpp_function from a vanilla function pointer
|
||||
template <typename Return, typename... Args, typename... Extra>
|
||||
cpp_function(Return (*f)(Args...), const Extra&... extra) {
|
||||
template <typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
cpp_function(Return (*f)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
|
||||
initialize(f, f, extra...);
|
||||
}
|
||||
|
||||
@ -57,17 +57,17 @@ public:
|
||||
}
|
||||
|
||||
/// Construct a cpp_function from a class method (non-const)
|
||||
template <typename Return, typename Class, typename... Arg, typename... Extra>
|
||||
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
|
||||
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
cpp_function(Return (Class::*f)(Arg...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
|
||||
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
||||
(Return (*) (Class *, Arg...)) nullptr, extra...);
|
||||
(Return (*) (Class *, Arg...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...);
|
||||
}
|
||||
|
||||
/// Construct a cpp_function from a class method (const)
|
||||
template <typename Return, typename Class, typename... Arg, typename... Extra>
|
||||
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
|
||||
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
cpp_function(Return (Class::*f)(Arg...) const PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
|
||||
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
|
||||
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
|
||||
(Return (*)(const Class *, Arg ...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...);
|
||||
}
|
||||
|
||||
/// Return the function name
|
||||
@ -80,8 +80,8 @@ protected:
|
||||
}
|
||||
|
||||
/// Special internal constructor for functors, lambda functions, etc.
|
||||
template <typename Func, typename Return, typename... Args, typename... Extra>
|
||||
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
|
||||
template <typename Func, typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
|
||||
void initialize(Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
|
||||
static_assert(detail::expected_num_args<Extra...>(sizeof...(Args)),
|
||||
"The number of named arguments does not match the function signature");
|
||||
|
||||
@ -160,7 +160,7 @@ protected:
|
||||
if (cast_in::has_kwargs) rec->has_kwargs = true;
|
||||
|
||||
/* Stash some additional information used by an important optimization in 'functional.h' */
|
||||
using FunctionType = Return (*)(Args...);
|
||||
using FunctionType = Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER;
|
||||
constexpr bool is_function_ptr =
|
||||
std::is_convertible<Func, FunctionType>::value &&
|
||||
sizeof(capture) == sizeof(void *);
|
||||
|
@ -41,6 +41,26 @@ std::string print_bytes(py::bytes bytes) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Test that we properly handle C++17 exception specifiers (which are part of the function signature
|
||||
// in C++17). These should all still work before C++17, but don't affect the function signature.
|
||||
namespace test_exc_sp {
|
||||
int f1(int x) noexcept { return x+1; }
|
||||
int f2(int x) noexcept(true) { return x+2; }
|
||||
int f3(int x) noexcept(false) { return x+3; }
|
||||
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
|
||||
struct C {
|
||||
int m1(int x) noexcept { return x-1; }
|
||||
int m2(int x) const noexcept { return x-2; }
|
||||
int m3(int x) noexcept(true) { return x-3; }
|
||||
int m4(int x) const noexcept(true) { return x-4; }
|
||||
int m5(int x) noexcept(false) { return x-5; }
|
||||
int m6(int x) const noexcept(false) { return x-6; }
|
||||
int m7(int x) throw() { return x-7; }
|
||||
int m8(int x) const throw() { return x-8; }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
test_initializer constants_and_functions([](py::module &m) {
|
||||
m.attr("some_constant") = py::int_(14);
|
||||
|
||||
@ -63,4 +83,22 @@ test_initializer constants_and_functions([](py::module &m) {
|
||||
|
||||
m.def("return_bytes", &return_bytes);
|
||||
m.def("print_bytes", &print_bytes);
|
||||
|
||||
using namespace test_exc_sp;
|
||||
py::module m2 = m.def_submodule("exc_sp");
|
||||
py::class_<C>(m2, "C")
|
||||
.def(py::init<>())
|
||||
.def("m1", &C::m1)
|
||||
.def("m2", &C::m2)
|
||||
.def("m3", &C::m3)
|
||||
.def("m4", &C::m4)
|
||||
.def("m5", &C::m5)
|
||||
.def("m6", &C::m6)
|
||||
.def("m7", &C::m7)
|
||||
.def("m8", &C::m8)
|
||||
;
|
||||
m2.def("f1", f1);
|
||||
m2.def("f2", f2);
|
||||
m2.def("f3", f3);
|
||||
m2.def("f4", f4);
|
||||
});
|
||||
|
@ -22,3 +22,22 @@ def test_bytes():
|
||||
from pybind11_tests import return_bytes, print_bytes
|
||||
|
||||
assert print_bytes(return_bytes()) == "bytes[1 0 2 0]"
|
||||
|
||||
|
||||
def test_exception_specifiers():
|
||||
from pybind11_tests.exc_sp import C, f1, f2, f3, f4
|
||||
|
||||
c = C()
|
||||
assert c.m1(2) == 1
|
||||
assert c.m2(3) == 1
|
||||
assert c.m3(5) == 2
|
||||
assert c.m4(7) == 3
|
||||
assert c.m5(10) == 5
|
||||
assert c.m6(14) == 8
|
||||
assert c.m7(20) == 13
|
||||
assert c.m8(29) == 21
|
||||
|
||||
assert f1(33) == 34
|
||||
assert f2(53) == 55
|
||||
assert f3(86) == 89
|
||||
assert f4(140) == 144
|
||||
|
Loading…
Reference in New Issue
Block a user