Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bd45ad0
Strip noexcept from cpp17 function type bindings
Skylion007 Feb 20, 2026
b303194
Fix a bug and increase test coverage
Skylion007 Feb 20, 2026
f530adb
Does this fix it?
Skylion007 Feb 20, 2026
70170b0
Silence clang-tidy issue
Skylion007 Feb 20, 2026
ba63458
Simplify method adapter with macro and add missing rvalue adaptors + …
Skylion007 Feb 21, 2026
48f561f
Supress clang-tidy errors
Skylion007 Feb 21, 2026
f9863d1
Improve test coverage
Skylion007 Feb 21, 2026
a329095
Add additional static assert
Skylion007 Feb 21, 2026
0e0669d
Try to resolve MSVC C4003 warning
Skylion007 Feb 21, 2026
b6092e9
Simplify method adaptor into 2 template instatiations with enable_if_t
Skylion007 Feb 21, 2026
e4ece99
Fix ambiguous STL template
Skylion007 Feb 21, 2026
798e516
Close remaining qualifier consistency gaps for member pointer bindings.
rwgk Feb 23, 2026
35584e0
Clarify why def_buffer/vectorize omit rvalue-qualified overloads.
rwgk Feb 23, 2026
94e6b4b
Add compile-only overload_cast guard for ref-qualified methods.
rwgk Feb 23, 2026
3914a28
Refactor overload_cast_impl qualifier overloads with a macro.
rwgk Feb 23, 2026
cd6ef9d
Expose __cpp_noexcept_function_type to Python tests and use explicit …
rwgk Feb 23, 2026
d80bfd9
Merge branch 'master' into Skylion007→skylion007/cpp17-claude-noexcep…
rwgk Mar 21, 2026
1d83b6f
Add static_assert in method_adaptor to guard that T is a member funct…
rwgk Mar 21, 2026
9fe879b
Merge branch 'master' into Skylion007→skylion007/cpp17-claude-noexcep…
rwgk Mar 22, 2026
a5403e7
automatic clang-format change (because of #6002)
rwgk Mar 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions include/pybind11/detail/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -1056,14 +1056,30 @@ struct strip_function_object {
using type = typename remove_class<decltype(&F::operator())>::type;
};

// Strip noexcept from a free function type (C++17: noexcept is part of the type).
template <typename T>
struct remove_noexcept {
using type = T;
};
#ifdef __cpp_noexcept_function_type
template <typename R, typename... A>
struct remove_noexcept<R(A...) noexcept> {
using type = R(A...);
};
#endif
template <typename T>
using remove_noexcept_t = typename remove_noexcept<T>::type;

// Extracts the function signature from a function, function pointer or lambda.
// Strips noexcept from the result so that factory/pickle_factory partial specializations
// (which match plain Return(Args...)) work correctly with noexcept callables (issue #2234).
template <typename Function, typename F = remove_reference_t<Function>>
using function_signature_t = conditional_t<
using function_signature_t = remove_noexcept_t<conditional_t<
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I normalize it here for all pybind11? Or keep it as utility that and create a new alias that always strips the noexcept

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the flip side we could use the noexcept tag handling to optimize away exception handling for certain edge cases like def_buffer maybe?

std::is_function<F>::value,
F,
typename conditional_t<std::is_pointer<F>::value || std::is_member_pointer<F>::value,
std::remove_pointer<F>,
strip_function_object<F>>::type>;
strip_function_object<F>>::type>>;

/// Returns true if the type looks like a lambda: that is, isn't a function, pointer or member
/// pointer. Note that this can catch all sorts of other things, too; this is intended to be used
Expand Down Expand Up @@ -1212,6 +1228,36 @@ struct overload_cast_impl {
-> decltype(pmf) {
return pmf;
}

// Define const/non-const member-pointer selector pairs for qualifier combinations.
// The `qualifiers` parameter is used in type position, where extra parentheses are invalid.
// NOLINTBEGIN(bugprone-macro-parentheses)
#define PYBIND11_OVERLOAD_CAST_MEMBER_PTR(qualifiers) \
template <typename Return, typename Class> \
constexpr auto operator()(Return (Class::*pmf)(Args...) qualifiers, std::false_type = {}) \
const noexcept -> decltype(pmf) { \
return pmf; \
} \
template <typename Return, typename Class> \
constexpr auto operator()(Return (Class::*pmf)(Args...) const qualifiers, std::true_type) \
const noexcept -> decltype(pmf) { \
return pmf; \
}
PYBIND11_OVERLOAD_CAST_MEMBER_PTR(&)
PYBIND11_OVERLOAD_CAST_MEMBER_PTR(&&)

#ifdef __cpp_noexcept_function_type
template <typename Return>
constexpr auto operator()(Return (*pf)(Args...) noexcept) const noexcept -> decltype(pf) {
return pf;
}

PYBIND11_OVERLOAD_CAST_MEMBER_PTR(noexcept)
PYBIND11_OVERLOAD_CAST_MEMBER_PTR(& noexcept)
PYBIND11_OVERLOAD_CAST_MEMBER_PTR(&& noexcept)
#endif
#undef PYBIND11_OVERLOAD_CAST_MEMBER_PTR
// NOLINTEND(bugprone-macro-parentheses)
};
PYBIND11_NAMESPACE_END(detail)

Expand Down
82 changes: 82 additions & 0 deletions include/pybind11/numpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -2327,4 +2327,86 @@ Helper vectorize(Return (Class::*f)(Args...) const) {
return Helper(std::mem_fn(f));
}

// Intentionally no &&/const&& overloads: vectorized method calls operate on the bound Python
// instance and should not consume/move-from self.
// Vectorize a class method (non-const, lvalue ref-qualified):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) &>())),
Return,
Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) &) {
return Helper(std::mem_fn(f));
}

// Vectorize a class method (const, lvalue ref-qualified):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const &>())),
Return,
const Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) const &) {
return Helper(std::mem_fn(f));
}

#ifdef __cpp_noexcept_function_type
// Vectorize a class method (non-const, noexcept):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) noexcept>())),
Return,
Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) noexcept) {
return Helper(std::mem_fn(f));
}

// Vectorize a class method (const, noexcept):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const noexcept>())),
Return,
const Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) const noexcept) {
return Helper(std::mem_fn(f));
}

// Vectorize a class method (non-const, lvalue ref-qualified, noexcept):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) & noexcept>())),
Return,
Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) & noexcept) {
return Helper(std::mem_fn(f));
}

// Vectorize a class method (const, lvalue ref-qualified, noexcept):
template <typename Return,
typename Class,
typename... Args,
typename Helper = detail::vectorize_helper<
decltype(std::mem_fn(std::declval<Return (Class::*)(Args...) const & noexcept>())),
Return,
const Class *,
Args...>>
Helper vectorize(Return (Class::*f)(Args...) const & noexcept) {
return Helper(std::mem_fn(f));
}
#endif

PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
Loading
Loading