Revert noexcept deduction in favour of better SFINAE on lambda functions (#677)

noexcept deduction, added in PR #555, doesn't work with clang's
-std=c++1z; and while it works with g++, it isn't entirely clear to me
that it is required to work in C++17.

What should work, however, is that C++17 allows implicit conversion of a
`noexcept(true)` function pointer to a `noexcept(false)` (i.e.  default,
noexcept-not-specified) function pointer.  That was breaking in pybind11
because the cpp_function template used for lambdas provided a better
match (i.e. without requiring an implicit conversion), but it then
failed.

This commit takes a different approach of using SFINAE on the lambda
function to prevent it from matching a non-lambda object, which then
gets implicit conversion from a `noexcept` function pointer to a
`noexcept(false)` function pointer.  This much nicer solution also gets
rid of the C++17 NOEXCEPT macros, and works in both clang and g++.
This commit is contained in:
Jason Rhinelander 2017-02-17 06:56:41 -05:00 committed by Wenzel Jakob
parent d361ea15fb
commit 1d7998e333
4 changed files with 33 additions and 44 deletions

View File

@ -202,17 +202,6 @@ extern "C" {
} \ } \
PyObject *pybind11_init() 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) NAMESPACE_BEGIN(pybind11)
using ssize_t = Py_ssize_t; using ssize_t = Py_ssize_t;
@ -643,16 +632,16 @@ struct nodelete { template <typename T> void operator()(T*) { } };
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
template <typename... Args> template <typename... Args>
struct overload_cast_impl { struct overload_cast_impl {
template <typename Return /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return>
constexpr auto operator()(Return (*pf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER) const noexcept constexpr auto operator()(Return (*pf)(Args...)) const noexcept
-> decltype(pf) { return pf; } -> decltype(pf) { return pf; }
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename Class>
constexpr auto operator()(Return (Class::*pmf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, std::false_type = {}) const noexcept constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
-> decltype(pmf) { return pmf; } -> decltype(pmf) { return pmf; }
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename Class>
constexpr auto operator()(Return (Class::*pmf)(Args...) const PYBIND11_NOEXCEPT_SPECIFIER, std::true_type) const noexcept constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept
-> decltype(pmf) { return pmf; } -> decltype(pmf) { return pmf; }
}; };
NAMESPACE_END(detail) NAMESPACE_END(detail)

View File

@ -15,11 +15,11 @@
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename... Args>
struct type_caster<std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>> { struct type_caster<std::function<Return(Args...)>> {
using type = std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>; using type = std::function<Return(Args...)>;
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>; using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
using function_type = Return (*) (Args...) PYBIND11_NOEXCEPT_SPECIFIER; using function_type = Return (*) (Args...);
public: public:
bool load(handle src_, bool) { bool load(handle src_, bool) {

View File

@ -1140,23 +1140,22 @@ template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
NAMESPACE_END(detail) NAMESPACE_END(detail)
template <typename Func, typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Func, typename Return, typename... Args>
detail::vectorize_helper<Func, Return, Args...> detail::vectorize_helper<Func, Return, Args...>
vectorize(const Func &f, Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) { vectorize(const Func &f, Return (*) (Args ...)) {
return detail::vectorize_helper<Func, Return, Args...>(f); return detail::vectorize_helper<Func, Return, Args...>(f);
} }
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename... Args>
detail::vectorize_helper<Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER, Return, Args...> detail::vectorize_helper<Return (*) (Args ...), Return, Args...>
vectorize(Return (*f) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) { vectorize(Return (*f) (Args ...)) {
return vectorize<Return (*) (Args ...), Return, Args...>(f, f); return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
} }
template <typename Func> template <typename Func, typename FuncType = typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type>
auto vectorize(Func &&f) -> decltype( auto vectorize(Func &&f) -> decltype(
vectorize(std::forward<Func>(f), (typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type *) nullptr)) { vectorize(std::forward<Func>(f), (FuncType *) nullptr)) {
return vectorize(std::forward<Func>(f), (typename detail::remove_class<decltype( return vectorize(std::forward<Func>(f), (FuncType *) nullptr);
&std::remove_reference<Func>::type::operator())>::type *) nullptr);
} }
NAMESPACE_END(pybind11) NAMESPACE_END(pybind11)

View File

@ -44,30 +44,31 @@ public:
cpp_function() { } cpp_function() { }
/// Construct a cpp_function from a vanilla function pointer /// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename... Args, typename... Extra>
cpp_function(Return (*f)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { cpp_function(Return (*f)(Args...), const Extra&... extra) {
initialize(f, f, extra...); initialize(f, f, extra...);
} }
/// Construct a cpp_function from a lambda function (possibly with internal state) /// Construct a cpp_function from a lambda function (possibly with internal state)
template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) { template <typename Func, typename... Extra,
typename FuncType = typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type>
cpp_function(Func &&f, const Extra&... extra) {
initialize(std::forward<Func>(f), initialize(std::forward<Func>(f),
(typename detail::remove_class<decltype( (FuncType *) nullptr, extra...);
&std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
} }
/// Construct a cpp_function from a class method (non-const) /// Construct a cpp_function from a class method (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...); (Return (*) (Class *, Arg...)) nullptr, extra...);
} }
/// Construct a cpp_function from a class method (const) /// Construct a cpp_function from a class method (const)
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...) const PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...); (Return (*)(const Class *, Arg ...)) nullptr, extra...);
} }
/// Return the function name /// Return the function name
@ -80,8 +81,8 @@ protected:
} }
/// Special internal constructor for functors, lambda functions, etc. /// Special internal constructor for functors, lambda functions, etc.
template <typename Func, typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG> template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
struct capture { typename std::remove_reference<Func>::type f; }; struct capture { typename std::remove_reference<Func>::type f; };
@ -162,7 +163,7 @@ protected:
if (cast_in::has_kwargs) rec->has_kwargs = true; if (cast_in::has_kwargs) rec->has_kwargs = true;
/* Stash some additional information used by an important optimization in 'functional.h' */ /* Stash some additional information used by an important optimization in 'functional.h' */
using FunctionType = Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER; using FunctionType = Return (*)(Args...);
constexpr bool is_function_ptr = constexpr bool is_function_ptr =
std::is_convertible<Func, FunctionType>::value && std::is_convertible<Func, FunctionType>::value &&
sizeof(capture) == sizeof(void *); sizeof(capture) == sizeof(void *);