Skip to content

Commit 2e425b2

Browse files
committed
Revert noexcept deduction in favour of better SFINAE on lambda functions
noexcept deduction, added in PR pybind#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++.
1 parent d361ea1 commit 2e425b2

File tree

4 files changed

+33
-44
lines changed

4 files changed

+33
-44
lines changed

include/pybind11/common.h

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,6 @@ extern "C" {
202202
} \
203203
PyObject *pybind11_init()
204204

205-
// Function return value and argument type deduction support. When compiling under C++17 these
206-
// differ as C++17 makes the noexcept specifier part of the function type, while it is not part of
207-
// the type under earlier standards.
208-
#ifdef __cpp_noexcept_function_type
209-
# define PYBIND11_NOEXCEPT_TPL_ARG , bool NoExceptions
210-
# define PYBIND11_NOEXCEPT_SPECIFIER noexcept(NoExceptions)
211-
#else
212-
# define PYBIND11_NOEXCEPT_TPL_ARG
213-
# define PYBIND11_NOEXCEPT_SPECIFIER
214-
#endif
215-
216205
NAMESPACE_BEGIN(pybind11)
217206

218207
using ssize_t = Py_ssize_t;
@@ -643,16 +632,16 @@ struct nodelete { template <typename T> void operator()(T*) { } };
643632
NAMESPACE_BEGIN(detail)
644633
template <typename... Args>
645634
struct overload_cast_impl {
646-
template <typename Return /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
647-
constexpr auto operator()(Return (*pf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER) const noexcept
635+
template <typename Return>
636+
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
648637
-> decltype(pf) { return pf; }
649638

650-
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
651-
constexpr auto operator()(Return (Class::*pmf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, std::false_type = {}) const noexcept
639+
template <typename Return, typename Class>
640+
constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
652641
-> decltype(pmf) { return pmf; }
653642

654-
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
655-
constexpr auto operator()(Return (Class::*pmf)(Args...) const PYBIND11_NOEXCEPT_SPECIFIER, std::true_type) const noexcept
643+
template <typename Return, typename Class>
644+
constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept
656645
-> decltype(pmf) { return pmf; }
657646
};
658647
NAMESPACE_END(detail)

include/pybind11/functional.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
NAMESPACE_BEGIN(pybind11)
1616
NAMESPACE_BEGIN(detail)
1717

18-
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
19-
struct type_caster<std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>> {
20-
using type = std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>;
18+
template <typename Return, typename... Args>
19+
struct type_caster<std::function<Return(Args...)>> {
20+
using type = std::function<Return(Args...)>;
2121
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
22-
using function_type = Return (*) (Args...) PYBIND11_NOEXCEPT_SPECIFIER;
22+
using function_type = Return (*) (Args...);
2323

2424
public:
2525
bool load(handle src_, bool) {

include/pybind11/numpy.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,23 +1140,22 @@ template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {
11401140

11411141
NAMESPACE_END(detail)
11421142

1143-
template <typename Func, typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
1143+
template <typename Func, typename Return, typename... Args>
11441144
detail::vectorize_helper<Func, Return, Args...>
1145-
vectorize(const Func &f, Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
1145+
vectorize(const Func &f, Return (*) (Args ...)) {
11461146
return detail::vectorize_helper<Func, Return, Args...>(f);
11471147
}
11481148

1149-
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
1150-
detail::vectorize_helper<Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER, Return, Args...>
1151-
vectorize(Return (*f) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
1149+
template <typename Return, typename... Args>
1150+
detail::vectorize_helper<Return (*) (Args ...), Return, Args...>
1151+
vectorize(Return (*f) (Args ...)) {
11521152
return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
11531153
}
11541154

1155-
template <typename Func>
1155+
template <typename Func, typename FuncType = typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type>
11561156
auto vectorize(Func &&f) -> decltype(
1157-
vectorize(std::forward<Func>(f), (typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type *) nullptr)) {
1158-
return vectorize(std::forward<Func>(f), (typename detail::remove_class<decltype(
1159-
&std::remove_reference<Func>::type::operator())>::type *) nullptr);
1157+
vectorize(std::forward<Func>(f), (FuncType *) nullptr)) {
1158+
return vectorize(std::forward<Func>(f), (FuncType *) nullptr);
11601159
}
11611160

11621161
NAMESPACE_END(pybind11)

include/pybind11/pybind11.h

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,30 +44,31 @@ class cpp_function : public function {
4444
cpp_function() { }
4545

4646
/// Construct a cpp_function from a vanilla function pointer
47-
template <typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
48-
cpp_function(Return (*f)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
47+
template <typename Return, typename... Args, typename... Extra>
48+
cpp_function(Return (*f)(Args...), const Extra&... extra) {
4949
initialize(f, f, extra...);
5050
}
5151

5252
/// Construct a cpp_function from a lambda function (possibly with internal state)
53-
template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) {
53+
template <typename Func, typename... Extra,
54+
typename FuncType = typename detail::remove_class<decltype(&std::remove_reference<Func>::type::operator())>::type>
55+
cpp_function(Func &&f, const Extra&... extra) {
5456
initialize(std::forward<Func>(f),
55-
(typename detail::remove_class<decltype(
56-
&std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
57+
(FuncType *) nullptr, extra...);
5758
}
5859

5960
/// Construct a cpp_function from a class method (non-const)
60-
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
61-
cpp_function(Return (Class::*f)(Arg...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
61+
template <typename Return, typename Class, typename... Arg, typename... Extra>
62+
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
6263
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
63-
(Return (*) (Class *, Arg...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...);
64+
(Return (*) (Class *, Arg...)) nullptr, extra...);
6465
}
6566

6667
/// Construct a cpp_function from a class method (const)
67-
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
68-
cpp_function(Return (Class::*f)(Arg...) const PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
68+
template <typename Return, typename Class, typename... Arg, typename... Extra>
69+
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
6970
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
70-
(Return (*)(const Class *, Arg ...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...);
71+
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
7172
}
7273

7374
/// Return the function name
@@ -80,8 +81,8 @@ class cpp_function : public function {
8081
}
8182

8283
/// Special internal constructor for functors, lambda functions, etc.
83-
template <typename Func, typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
84-
void initialize(Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
84+
template <typename Func, typename Return, typename... Args, typename... Extra>
85+
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
8586

8687
struct capture { typename std::remove_reference<Func>::type f; };
8788

@@ -162,7 +163,7 @@ class cpp_function : public function {
162163
if (cast_in::has_kwargs) rec->has_kwargs = true;
163164

164165
/* Stash some additional information used by an important optimization in 'functional.h' */
165-
using FunctionType = Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER;
166+
using FunctionType = Return (*)(Args...);
166167
constexpr bool is_function_ptr =
167168
std::is_convertible<Func, FunctionType>::value &&
168169
sizeof(capture) == sizeof(void *);

0 commit comments

Comments
 (0)