mirror of
https://github.com/pybind/pybind11.git
synced 2026-05-20 21:09:27 +00:00
fix: bind noexcept and ref-qualified methods from unregistered base classes (#5992)
* Strip noexcept from cpp17 function type bindings * Fix a bug and increase test coverage * Does this fix it? * Silence clang-tidy issue * Simplify method adapter with macro and add missing rvalue adaptors + tests * Supress clang-tidy errors * Improve test coverage * Add additional static assert * Try to resolve MSVC C4003 warning * Simplify method adaptor into 2 template instatiations with enable_if_t * Fix ambiguous STL template * Close remaining qualifier consistency gaps for member pointer bindings. A production-code review after #2234 showed that ref-qualified member pointers were still inconsistently handled across def_buffer, vectorize, and overload_cast, so this adds the missing overloads with focused tests for each newly-supported signature. Co-authored-by: Cursor <cursoragent@cursor.com> * Clarify why def_buffer/vectorize omit rvalue-qualified overloads. These comments were added while reviewing the qualifier coverage follow-up, to document that buffer/vectorized calls operate on existing Python-owned instances and should not move-from self. Co-authored-by: Cursor <cursoragent@cursor.com> * Add compile-only overload_cast guard for ref-qualified methods. This was added as a maintenance follow-up to the qualifier-consistency work, so future changes that introduce overload_cast ambiguity or wrong ref/noexcept resolution fail at compile time. Co-authored-by: Cursor <cursoragent@cursor.com> * Refactor overload_cast_impl qualifier overloads with a macro. As part of the qualifier-consistency maintenance follow-up, this reduces duplication in overload_cast_impl while preserving the same ref/noexcept coverage and keeping pedantic-clean macro expansion. Co-authored-by: Cursor <cursoragent@cursor.com> * Expose __cpp_noexcept_function_type to Python tests and use explicit skip guards. This replaces hasattr-based optional assertions with skipif-gated noexcept-only tests so skipped coverage is visible in pytest output while keeping non-noexcept checks always active. Co-authored-by: Cursor <cursoragent@cursor.com> * Add static_assert in method_adaptor to guard that T is a member function pointer. Suggested by @Skylion007 in PR #5992 review comment [T007]. Made-with: Cursor * automatic clang-format change (because of #6002) --------- Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
Ralf W. Grosse-Kunstleve
parent
c0cfa96555
commit
3cb5a763c1
@@ -439,4 +439,133 @@ TEST_SUBMODULE(buffers, m) {
|
||||
PyBuffer_Release(&buffer);
|
||||
return result;
|
||||
});
|
||||
|
||||
// test_noexcept_def_buffer (issue #2234)
|
||||
// def_buffer(Return (Class::*)(Args...) noexcept) and
|
||||
// def_buffer(Return (Class::*)(Args...) const noexcept) must compile and work correctly.
|
||||
struct OneDBuffer {
|
||||
// Declare m_data before m_n to match initialiser list order below.
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBuffer(py::ssize_t n) : m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBuffer() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) noexcept)
|
||||
py::buffer_info get_buffer() noexcept {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)});
|
||||
}
|
||||
};
|
||||
|
||||
// non-const noexcept member function form
|
||||
py::class_<OneDBuffer>(m, "OneDBuffer", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBuffer::get_buffer);
|
||||
|
||||
// const noexcept member function form (separate class to avoid ambiguity)
|
||||
struct OneDBufferConst {
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBufferConst(py::ssize_t n) : m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBufferConst() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) const noexcept)
|
||||
py::buffer_info get_buffer() const noexcept {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)},
|
||||
/*readonly=*/true);
|
||||
}
|
||||
};
|
||||
py::class_<OneDBufferConst>(m, "OneDBufferConst", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBufferConst::get_buffer);
|
||||
|
||||
// test_ref_qualified_def_buffer
|
||||
struct OneDBufferLRef {
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBufferLRef(py::ssize_t n) : m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBufferLRef() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) &)
|
||||
py::buffer_info get_buffer() & {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)});
|
||||
}
|
||||
};
|
||||
py::class_<OneDBufferLRef>(m, "OneDBufferLRef", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBufferLRef::get_buffer);
|
||||
|
||||
struct OneDBufferConstLRef {
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBufferConstLRef(py::ssize_t n) : m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBufferConstLRef() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) const &)
|
||||
py::buffer_info get_buffer() const & {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)},
|
||||
/*readonly=*/true);
|
||||
}
|
||||
};
|
||||
py::class_<OneDBufferConstLRef>(m, "OneDBufferConstLRef", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBufferConstLRef::get_buffer);
|
||||
|
||||
#ifdef __cpp_noexcept_function_type
|
||||
struct OneDBufferLRefNoexcept {
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBufferLRefNoexcept(py::ssize_t n) : m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBufferLRefNoexcept() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) & noexcept)
|
||||
py::buffer_info get_buffer() & noexcept {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)});
|
||||
}
|
||||
};
|
||||
py::class_<OneDBufferLRefNoexcept>(m, "OneDBufferLRefNoexcept", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBufferLRefNoexcept::get_buffer);
|
||||
|
||||
struct OneDBufferConstLRefNoexcept {
|
||||
float *m_data;
|
||||
py::ssize_t m_n;
|
||||
explicit OneDBufferConstLRefNoexcept(py::ssize_t n)
|
||||
: m_data(new float[(size_t) n]()), m_n(n) {}
|
||||
~OneDBufferConstLRefNoexcept() { delete[] m_data; }
|
||||
// Exercises def_buffer(Return (Class::*)(Args...) const & noexcept)
|
||||
py::buffer_info get_buffer() const & noexcept {
|
||||
return py::buffer_info(m_data,
|
||||
sizeof(float),
|
||||
py::format_descriptor<float>::format(),
|
||||
1,
|
||||
{m_n},
|
||||
{(py::ssize_t) sizeof(float)},
|
||||
/*readonly=*/true);
|
||||
}
|
||||
};
|
||||
py::class_<OneDBufferConstLRefNoexcept>(
|
||||
m, "OneDBufferConstLRefNoexcept", py::buffer_protocol())
|
||||
.def(py::init<py::ssize_t>())
|
||||
.def_buffer(&OneDBufferConstLRefNoexcept::get_buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user