From 7dd6396bd441b4fccd6f3eb503ea5959cd3e60ba Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Mon, 11 Apr 2022 12:56:38 -0700 Subject: [PATCH 01/16] Reapplied changes from #3674 --- docs/advanced/cast/custom.rst | 118 +++++++++++++++++------------ include/pybind11/cast.h | 42 ++++++++-- tests/CMakeLists.txt | 2 + tests/test_make_caster_adl.cpp | 89 ++++++++++++++++++++++ tests/test_make_caster_adl.py | 23 ++++++ tests/test_make_caster_adl_alt.cpp | 13 ++++ tests/test_make_caster_adl_alt.py | 7 ++ 7 files changed, 239 insertions(+), 55 deletions(-) create mode 100644 tests/test_make_caster_adl.cpp create mode 100644 tests/test_make_caster_adl.py create mode 100644 tests/test_make_caster_adl_alt.cpp create mode 100644 tests/test_make_caster_adl_alt.py diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 1df4d3e14b..401840346c 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -31,63 +31,81 @@ The following Python snippet demonstrates the intended usage from the Python sid print(A()) -To register the necessary conversion routines, it is necessary to add an -instantiation of the ``pybind11::detail::type_caster`` template. -Although this is an implementation detail, adding an instantiation of this -type is explicitly allowed. +To register the necessary conversion routines, it is necessary to define a +caster class, and register it with the other pybind11 casters: .. code-block:: cpp - namespace pybind11 { namespace detail { - template <> struct type_caster { - public: - /** - * This macro establishes the name 'inty' in - * function signatures and declares a local variable - * 'value' of type inty - */ - PYBIND11_TYPE_CASTER(inty, const_name("inty")); - - /** - * Conversion part 1 (Python->C++): convert a PyObject into a inty - * instance or return false upon failure. The second argument - * indicates whether implicit conversions should be applied. - */ - bool load(handle src, bool) { - /* Extract PyObject from handle */ - PyObject *source = src.ptr(); - /* Try converting into a Python integer value */ - PyObject *tmp = PyNumber_Long(source); - if (!tmp) - return false; - /* Now try to convert into a C++ int */ - value.long_value = PyLong_AsLong(tmp); - Py_DECREF(tmp); - /* Ensure return code was OK (to avoid out-of-range errors etc) */ - return !(value.long_value == -1 && !PyErr_Occurred()); - } - - /** - * Conversion part 2 (C++ -> Python): convert an inty instance into - * a Python object. The second and third arguments are used to - * indicate the return value policy and parent object (for - * ``return_value_policy::reference_internal``) and are generally - * ignored by implicit casters. - */ - static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) { - return PyLong_FromLong(src.long_value); - } - }; - }} // namespace pybind11::detail + struct inty_type_caster { + public: + /** + * This macro establishes the name 'inty' in + * function signatures and declares a local variable + * 'value' of type inty + */ + PYBIND11_TYPE_CASTER(inty, const_name("inty")); + /** + * Conversion part 1 (Python->C++): convert a PyObject into a inty + * instance or return false upon failure. The second argument + * indicates whether implicit conversions should be applied. + */ + bool load(handle src, bool) { + /* Extract PyObject from handle */ + PyObject *source = src.ptr(); + /* Try converting into a Python integer value */ + PyObject *tmp = PyNumber_Long(source); + if (!tmp) + return false; + /* Now try to convert into a C++ int */ + value.long_value = PyLong_AsLong(tmp); + Py_DECREF(tmp); + /* Ensure return code was OK (to avoid out-of-range errors etc) */ + return !(value.long_value == -1 && !PyErr_Occurred()); + } + /** + * Conversion part 2 (C++ -> Python): convert an inty instance into + * a Python object. The second and third arguments are used to + * indicate the return value policy and parent object (for + * ``return_value_policy::reference_internal``) and are generally + * ignored by implicit casters. + */ + static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) { + return PyLong_FromLong(src.long_value); + } + }; .. note:: - A ``type_caster`` defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires + A caster class defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires that ``T`` is default-constructible (``value`` is first default constructed and then ``load()`` assigns to it). -.. warning:: +The caster defined above must be registered with pybind11. +There are two ways to do it: - When using custom type casters, it's important to declare them consistently - in every compilation unit of the Python extension module. Otherwise, - undefined behavior can ensue. +* As an instantiation of the ``pybind11::detail::type_caster`` template. + Although this is an implementation detail, adding an instantiation of this + type is explicitly allowed: + + .. code-block:: cpp + + namespace pybind11 { namespace detail { + template <> struct type_caster : inty_type_caster {}; + }} // namespace pybind11::detail + + .. warning:: + + When using this method, it's important to declare them consistently + in every compilation unit of the Python extension module. Otherwise, + undefined behavior can ensue. + +* When you own the namespace where the type is defined, the preferred method + is to *declare* a function named ``pybind11_select_caster``, its only purpose + is to associate the C++ type with its caster class: + + .. code-block:: cpp + + inty_type_caster pybind11_select_caster(inty*); + + The argument is a *pointer* to the C++ type, the return type is the + caster type. This function has no implementation! diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 376a679544..bc46b63f3f 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -33,8 +33,34 @@ PYBIND11_NAMESPACE_BEGIN(detail) template class type_caster : public type_caster_base {}; + +template type_caster pybind11_select_caster(IntrinsicType *); + +// MSVC 2015 generates an internal compiler error for the common code (in the #else branch below). +// MSVC 2017 in C++14 mode produces incorrect code, leading to a tests/test_stl runtime failure. +// Luckily, the workaround for MSVC 2015 also resolves the MSVC 2017 C++14 runtime failure. +#if defined(_MSC_VER) && (_MSC_VER < 1910 || (_MSC_VER < 1920 && !defined(PYBIND11_CPP17))) + +template +struct make_caster_impl; + +template +struct make_caster_impl::value>> +: type_caster {}; + +template +struct make_caster_impl::value>> +: decltype(pybind11_select_caster(static_cast(nullptr))) {}; + template -using make_caster = type_caster>; +using make_caster = make_caster_impl>; + +#else + +template +using make_caster = decltype(pybind11_select_caster(static_cast *>(nullptr))); + +#endif // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T template @@ -1007,8 +1033,8 @@ struct return_value_policy_override< }; // Basic python -> C++ casting; throws if casting fails -template -type_caster &load_type(type_caster &conv, const handle &handle) { +template +Caster &load_type(Caster& conv, const handle& handle) { if (!conv.load(handle, true)) { #if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) throw cast_error("Unable to cast Python instance to C++ type (#define " @@ -1021,11 +1047,17 @@ type_caster &load_type(type_caster &conv, const handle &ha } return conv; } + +template +type_caster& load_type(type_caster& conv, const handle& handle) { + return load_type>(conv, handle); +} + // Wrapper around the above that also constructs and returns a type_caster template make_caster load_type(const handle &handle) { make_caster conv; - load_type(conv, handle); + load_type(conv, handle); return conv; } @@ -1152,7 +1184,7 @@ using override_caster_t = conditional_t enable_if_t::value, T> cast_ref(object &&o, make_caster &caster) { - return cast_op(load_type(caster, o)); + return cast_op(load_type(caster, o)); } template enable_if_t::value, T> cast_ref(object &&, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bce8cceb1e..7275152640 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -137,6 +137,8 @@ set(PYBIND11_TEST_FILES test_iostream test_kwargs_and_defaults test_local_bindings + test_make_caster_adl + test_make_caster_adl_alt test_methods_and_attributes test_modules test_multiple_inheritance diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp new file mode 100644 index 0000000000..2cb00b7a83 --- /dev/null +++ b/tests/test_make_caster_adl.cpp @@ -0,0 +1,89 @@ +// adl = Argument Dependent Lookup + +#include "pybind11_tests.h" + +namespace have_a_ns { +struct type_mock {}; +struct mock_caster { + static int num() { return 101; } +}; +mock_caster pybind11_select_caster(type_mock *); +} // namespace have_a_ns + +// namespace global { +struct global_ns_type_mock {}; +struct global_ns_mock_caster { + static int num() { return 202; } +}; +global_ns_mock_caster pybind11_select_caster(global_ns_type_mock *); +// } // namespace global + +namespace { +struct unnamed_ns_type_mock {}; +struct unnamed_ns_mock_caster { + static int num() { return 303; } +}; +unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); +#if !defined(_MSC_VER) +// Dummy implementation, purely to avoid compiler warnings (unused function). +// Easier than managing compiler-specific pragmas for warning suppression. +// MSVC happens to not generate any warnings. Leveraging that to prove that +// this test actually works without an implementation. +unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *) { + return unnamed_ns_mock_caster{}; +} +#endif +} // namespace + +namespace mrc_ns { // minimal real caster + +struct type_mrc { + int value = -9999; +}; + +struct minimal_real_caster { + static constexpr auto name = py::detail::const_name(); + + static py::handle + cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) { + return py::int_(src.value + 1000).release(); + } + + // Maximizing simplicity. This will go terribly wrong for other arg types. + template + using cast_op_type = const type_mrc &; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator type_mrc const &() { + static type_mrc obj; + obj.value = 404; + return obj; + } + + bool load(py::handle src, bool /*convert*/) { + // Only accepts str, but the value is ignored. + return py::isinstance(src); + } +}; + +minimal_real_caster pybind11_select_caster(type_mrc *); + +} // namespace mrc_ns + +TEST_SUBMODULE(make_caster_adl, m) { + m.def("have_a_ns_num", []() { return py::detail::make_caster::num(); }); + m.def("global_ns_num", []() { return py::detail::make_caster::num(); }); + m.def("unnamed_ns_num", []() { return py::detail::make_caster::num(); }); + + m.def("mrc_return", []() { + mrc_ns::type_mrc obj; + obj.value = 505; + return obj; + }); + m.def("mrc_arg", [](mrc_ns::type_mrc const &obj) { return obj.value + 2000; }); + +#if !defined(_MSC_VER) + // Dummy call, purely to avoid compiler warnings (unused function). + (void) pybind11_select_caster(static_cast(nullptr)); +#endif +} \ No newline at end of file diff --git a/tests/test_make_caster_adl.py b/tests/test_make_caster_adl.py new file mode 100644 index 0000000000..64615a8148 --- /dev/null +++ b/tests/test_make_caster_adl.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +import pytest + +from pybind11_tests import make_caster_adl as m +from pybind11_tests import make_caster_adl_alt as m_alt + + +def test_mock_casters(): + assert m.have_a_ns_num() == 101 + assert m.global_ns_num() == 202 + assert m.unnamed_ns_num() == 303 + + +def test_mock_casters_alt(): + assert m_alt.have_a_ns_num() == 121 + + +def test_minimal_real_caster(): + assert m.mrc_return() == 1505 + assert m.mrc_arg(u"ignored") == 2404 + with pytest.raises(TypeError) as excinfo: + m.mrc_arg(None) + assert "(arg0: mrc_ns::type_mrc) -> int" in str(excinfo.value) \ No newline at end of file diff --git a/tests/test_make_caster_adl_alt.cpp b/tests/test_make_caster_adl_alt.cpp new file mode 100644 index 0000000000..1feb5be50e --- /dev/null +++ b/tests/test_make_caster_adl_alt.cpp @@ -0,0 +1,13 @@ +#include "pybind11_tests.h" + +namespace have_a_ns { +struct type_mock {}; +struct mock_caster_alt { + static int num() { return 121; } +}; +mock_caster_alt pybind11_select_caster(type_mock *); +} // namespace have_a_ns + +TEST_SUBMODULE(make_caster_adl_alt, m) { + m.def("have_a_ns_num", []() { return py::detail::make_caster::num(); }); +} diff --git a/tests/test_make_caster_adl_alt.py b/tests/test_make_caster_adl_alt.py new file mode 100644 index 0000000000..a21220203d --- /dev/null +++ b/tests/test_make_caster_adl_alt.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +from pybind11_tests import make_caster_adl_alt as m + + +def test_mock_casters(): + assert m.have_a_ns_num() == 121 \ No newline at end of file From 93f1577543c7bdf19bb6711142a559bcfc4183f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 20:04:49 +0000 Subject: [PATCH 02/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- include/pybind11/cast.h | 13 +++++++------ tests/test_make_caster_adl.cpp | 2 +- tests/test_make_caster_adl.py | 5 ++--- tests/test_make_caster_adl_alt.py | 4 +--- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index bc46b63f3f..af1a7fc34e 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -34,7 +34,8 @@ PYBIND11_NAMESPACE_BEGIN(detail) template class type_caster : public type_caster_base {}; -template type_caster pybind11_select_caster(IntrinsicType *); +template +type_caster pybind11_select_caster(IntrinsicType *); // MSVC 2015 generates an internal compiler error for the common code (in the #else branch below). // MSVC 2017 in C++14 mode produces incorrect code, leading to a tests/test_stl runtime failure. @@ -45,12 +46,12 @@ template struct make_caster_impl; template -struct make_caster_impl::value>> -: type_caster {}; +struct make_caster_impl::value>> + : type_caster {}; template struct make_caster_impl::value>> -: decltype(pybind11_select_caster(static_cast(nullptr))) {}; + : decltype(pybind11_select_caster(static_cast(nullptr))) {}; template using make_caster = make_caster_impl>; @@ -1034,7 +1035,7 @@ struct return_value_policy_override< // Basic python -> C++ casting; throws if casting fails template -Caster &load_type(Caster& conv, const handle& handle) { +Caster &load_type(Caster &conv, const handle &handle) { if (!conv.load(handle, true)) { #if !defined(PYBIND11_DETAILED_ERROR_MESSAGES) throw cast_error("Unable to cast Python instance to C++ type (#define " @@ -1049,7 +1050,7 @@ Caster &load_type(Caster& conv, const handle& handle) { } template -type_caster& load_type(type_caster& conv, const handle& handle) { +type_caster &load_type(type_caster &conv, const handle &handle) { return load_type>(conv, handle); } diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 2cb00b7a83..0fe66b62c9 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -86,4 +86,4 @@ TEST_SUBMODULE(make_caster_adl, m) { // Dummy call, purely to avoid compiler warnings (unused function). (void) pybind11_select_caster(static_cast(nullptr)); #endif -} \ No newline at end of file +} diff --git a/tests/test_make_caster_adl.py b/tests/test_make_caster_adl.py index 64615a8148..b53e2e884c 100644 --- a/tests/test_make_caster_adl.py +++ b/tests/test_make_caster_adl.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import pytest from pybind11_tests import make_caster_adl as m @@ -17,7 +16,7 @@ def test_mock_casters_alt(): def test_minimal_real_caster(): assert m.mrc_return() == 1505 - assert m.mrc_arg(u"ignored") == 2404 + assert m.mrc_arg("ignored") == 2404 with pytest.raises(TypeError) as excinfo: m.mrc_arg(None) - assert "(arg0: mrc_ns::type_mrc) -> int" in str(excinfo.value) \ No newline at end of file + assert "(arg0: mrc_ns::type_mrc) -> int" in str(excinfo.value) diff --git a/tests/test_make_caster_adl_alt.py b/tests/test_make_caster_adl_alt.py index a21220203d..0170a4e108 100644 --- a/tests/test_make_caster_adl_alt.py +++ b/tests/test_make_caster_adl_alt.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- - from pybind11_tests import make_caster_adl_alt as m def test_mock_casters(): - assert m.have_a_ns_num() == 121 \ No newline at end of file + assert m.have_a_ns_num() == 121 From 47eab84e81a1f98c26a0426fab086834c59ed2a1 Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Mon, 11 Apr 2022 13:54:56 -0700 Subject: [PATCH 03/16] Update tests to use friend functions. Friend functions are preferable for ADL-based selection of the type casters. --- tests/test_make_caster_adl.cpp | 11 ++++++++--- tests/test_make_caster_adl_alt.cpp | 6 ++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 0fe66b62c9..880643b15c 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -3,11 +3,14 @@ #include "pybind11_tests.h" namespace have_a_ns { -struct type_mock {}; struct mock_caster { static int num() { return 101; } }; -mock_caster pybind11_select_caster(type_mock *); +struct type_mock { + friend mock_caster pybind11_select_caster(type_mock *); +}; + + } // namespace have_a_ns // namespace global { @@ -37,8 +40,11 @@ unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *) { namespace mrc_ns { // minimal real caster +struct minimal_real_caster; + struct type_mrc { int value = -9999; + friend minimal_real_caster pybind11_select_caster(type_mrc *); }; struct minimal_real_caster { @@ -66,7 +72,6 @@ struct minimal_real_caster { } }; -minimal_real_caster pybind11_select_caster(type_mrc *); } // namespace mrc_ns diff --git a/tests/test_make_caster_adl_alt.cpp b/tests/test_make_caster_adl_alt.cpp index 1feb5be50e..b06af75ead 100644 --- a/tests/test_make_caster_adl_alt.cpp +++ b/tests/test_make_caster_adl_alt.cpp @@ -1,11 +1,13 @@ #include "pybind11_tests.h" namespace have_a_ns { -struct type_mock {}; struct mock_caster_alt { static int num() { return 121; } }; -mock_caster_alt pybind11_select_caster(type_mock *); +struct type_mock { + friend mock_caster_alt pybind11_select_caster(type_mock *); +}; + } // namespace have_a_ns TEST_SUBMODULE(make_caster_adl_alt, m) { From cb9fe3e0dd36aade1349586aecec870d4509f4f7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 20:57:36 +0000 Subject: [PATCH 04/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_make_caster_adl.cpp | 4 +--- tests/test_make_caster_adl_alt.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 880643b15c..f5c8a1ee58 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -7,10 +7,9 @@ struct mock_caster { static int num() { return 101; } }; struct type_mock { - friend mock_caster pybind11_select_caster(type_mock *); + friend mock_caster pybind11_select_caster(type_mock *); }; - } // namespace have_a_ns // namespace global { @@ -72,7 +71,6 @@ struct minimal_real_caster { } }; - } // namespace mrc_ns TEST_SUBMODULE(make_caster_adl, m) { diff --git a/tests/test_make_caster_adl_alt.cpp b/tests/test_make_caster_adl_alt.cpp index b06af75ead..b19851708a 100644 --- a/tests/test_make_caster_adl_alt.cpp +++ b/tests/test_make_caster_adl_alt.cpp @@ -5,7 +5,7 @@ struct mock_caster_alt { static int num() { return 121; } }; struct type_mock { - friend mock_caster_alt pybind11_select_caster(type_mock *); + friend mock_caster_alt pybind11_select_caster(type_mock *); }; } // namespace have_a_ns From 62bb9dfb4f1fee4f821a4b60422f5b9367eceae4 Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Mon, 11 Apr 2022 13:59:29 -0700 Subject: [PATCH 05/16] Update doc to demonstrate friend function --- docs/advanced/cast/custom.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 401840346c..7e46f98394 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -105,7 +105,12 @@ There are two ways to do it: .. code-block:: cpp - inty_type_caster pybind11_select_caster(inty*); + struct inty_type_caster; + + struct inty { + ... + friend inty_type_caster pybind11_select_caster(inty*); + } The argument is a *pointer* to the C++ type, the return type is the caster type. This function has no implementation! From 0f17baea3b0ca894ae3493dfea098d4d80346c4b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 5 May 2022 19:30:41 -0700 Subject: [PATCH 06/16] More-or-less overhaul of custom.rst --- docs/advanced/cast/custom.rst | 104 +++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 7e46f98394..9c2d2abace 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -1,41 +1,37 @@ Custom type casters =================== -In very rare cases, applications may require custom type casters that cannot be -expressed using the abstractions provided by pybind11, thus requiring raw -Python C API calls. This is fairly advanced usage and should only be pursued by -experts who are familiar with the intricacies of Python reference counting. +Some applications may prefer custom type casters that convert between existing +Python types and C++ types, similar to the ``list`` ↔ ``std::vector`` +and ``dict`` ↔ ``std::map`` conversions which are built into pybind11. +Implementing custom type casters is fairly advanced usage and requires +familiarity with the intricacies of the Python C API. The following snippets demonstrate how this works for a very simple ``inty`` -type that that should be convertible from Python types that provide a -``__int__(self)`` method. +type that we want to be convertible to C++ from any Python type that provides +an ``__int__`` method, and is converted to a Python ``int`` when returned from +C++ to Python. .. code-block:: cpp - struct inty { long long_value; }; - - void print(inty s) { - std::cout << s.long_value << std::endl; - } - -The following Python snippet demonstrates the intended usage from the Python side: + namespace user_space { -.. code-block:: python - - class A: - def __int__(self): - return 123 + struct inty { long long_value; }; + void print(inty s) { std::cout << s.long_value << std::endl; } - from example import print + inty return_42() { return inty{42}; } - print(A()) + } // namespace user_space -To register the necessary conversion routines, it is necessary to define a -caster class, and register it with the other pybind11 casters: +The necessary conversion routines are defined by a caster class, which +is then "plugged into" pybind11 using one of two alternative mechanisms. +Starting with the example caster class: .. code-block:: cpp + namespace user_space { + struct inty_type_caster { public: /** @@ -74,43 +70,61 @@ caster class, and register it with the other pybind11 casters: } }; + } // namespace user_space + .. note:: - A caster class defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires + A caster class using with ``PYBIND11_TYPE_CASTER(T, ...)`` requires that ``T`` is default-constructible (``value`` is first default constructed - and then ``load()`` assigns to it). + and then ``load()`` assigns to it). It is possible but more involved to define + a caster class for types that are not default-constructible. -The caster defined above must be registered with pybind11. -There are two ways to do it: +The caster class defined above can be plugged into pybind11 in two ways: -* As an instantiation of the ``pybind11::detail::type_caster`` template. - Although this is an implementation detail, adding an instantiation of this - type is explicitly allowed: +* Starting with pybind11 v2.10, a new — and the recommended — alternative is to *declare* a + function named ``pybind11_select_caster``: .. code-block:: cpp - namespace pybind11 { namespace detail { - template <> struct type_caster : inty_type_caster {}; - }} // namespace pybind11::detail + namespace user_space { - .. warning:: + inty_type_caster pybind11_select_caster(inty*); - When using this method, it's important to declare them consistently - in every compilation unit of the Python extension module. Otherwise, - undefined behavior can ensue. + } // namespace user_space -* When you own the namespace where the type is defined, the preferred method - is to *declare* a function named ``pybind11_select_caster``, its only purpose - is to associate the C++ type with its caster class: + The argument is a *pointer* to the C++ type, the return type is the caster type. + This function has no implementation! Its only purpose is to associate the C++ type + with its caster class. pybind11 exploits C++ Argument Dependent Lookup + (`ADL `_) + to discover the association. - .. code-block:: cpp + Note that ``pybind11_select_caster`` can alternatively be declared as a ``friend`` + function of the C++ type, if that is practical and preferred: - struct inty_type_caster; + .. code-block:: cpp struct inty { - ... - friend inty_type_caster pybind11_select_caster(inty*); + ... + friend inty_type_caster pybind11_select_caster(inty*); } - The argument is a *pointer* to the C++ type, the return type is the - caster type. This function has no implementation! +* An older alternative is to specialize the ``pybind11::detail::type_caster`` template. + Although the ``detail`` namespace is involved, adding a ``type_caster`` specialization + is explicitly allowed: + + .. code-block:: cpp + + namespace pybind11 { namespace detail { + template <> struct type_caster : user_space::inty_type_caster {}; + }} // namespace pybind11::detail + + .. note:: + ``type_caster` specializations may be full (as in this simple example) or partial. + + .. warning:: + + When using this method, it is important to declare the specializations + consistently in all compilation units of a Python extension module. + Otherwise the One Definition Rule + (`ODR `_) + is violated, which can result in undefined behavior. From 7924f189d67e77361e6c88786bd3b03c9813b9f6 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 5 May 2022 19:42:19 -0700 Subject: [PATCH 07/16] Misc minor C++ code enhancements. --- docs/advanced/cast/custom.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 9c2d2abace..fb041f8d43 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -50,13 +50,18 @@ Starting with the example caster class: PyObject *source = src.ptr(); /* Try converting into a Python integer value */ PyObject *tmp = PyNumber_Long(source); - if (!tmp) + if (!tmp) { return false; + } /* Now try to convert into a C++ int */ value.long_value = PyLong_AsLong(tmp); Py_DECREF(tmp); /* Ensure return code was OK (to avoid out-of-range errors etc) */ - return !(value.long_value == -1 && !PyErr_Occurred()); + if (PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + return true; } /** * Conversion part 2 (C++ -> Python): convert an inty instance into @@ -115,7 +120,7 @@ The caster class defined above can be plugged into pybind11 in two ways: .. code-block:: cpp namespace pybind11 { namespace detail { - template <> struct type_caster : user_space::inty_type_caster {}; + template <> struct type_caster : user_space::inty_type_caster {}; }} // namespace pybind11::detail .. note:: From a29e3527501465d4cafdf05e38a0985110fa0f35 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 12:51:50 -0700 Subject: [PATCH 08/16] custom.rst reviewer-suggested changes & misc tweaks. --- docs/advanced/cast/custom.rst | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index fb041f8d43..d1bc902ead 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -39,13 +39,13 @@ Starting with the example caster class: * function signatures and declares a local variable * 'value' of type inty */ - PYBIND11_TYPE_CASTER(inty, const_name("inty")); + PYBIND11_TYPE_CASTER(inty, pybind11::const_name("inty")); /** * Conversion part 1 (Python->C++): convert a PyObject into a inty * instance or return false upon failure. The second argument * indicates whether implicit conversions should be applied. */ - bool load(handle src, bool) { + bool load(pybind11::handle src, bool) { /* Extract PyObject from handle */ PyObject *source = src.ptr(); /* Try converting into a Python integer value */ @@ -58,8 +58,8 @@ Starting with the example caster class: Py_DECREF(tmp); /* Ensure return code was OK (to avoid out-of-range errors etc) */ if (PyErr_Occurred()) { - PyErr_Clear(); - return false; + PyErr_Clear(); + return false; } return true; } @@ -70,7 +70,9 @@ Starting with the example caster class: * ``return_value_policy::reference_internal``) and are generally * ignored by implicit casters. */ - static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) { + static pybind11::handle cast(inty src, + pybind11::return_value_policy /* policy */, + pybind11::handle /* parent */) { return PyLong_FromLong(src.long_value); } }; @@ -79,7 +81,7 @@ Starting with the example caster class: .. note:: - A caster class using with ``PYBIND11_TYPE_CASTER(T, ...)`` requires + A caster class using ``PYBIND11_TYPE_CASTER(T, ...)`` requires that ``T`` is default-constructible (``value`` is first default constructed and then ``load()`` assigns to it). It is possible but more involved to define a caster class for types that are not default-constructible. @@ -108,10 +110,12 @@ The caster class defined above can be plugged into pybind11 in two ways: .. code-block:: cpp + struct inty_type_caster; + struct inty { ... friend inty_type_caster pybind11_select_caster(inty*); - } + }; * An older alternative is to specialize the ``pybind11::detail::type_caster`` template. Although the ``detail`` namespace is involved, adding a ``type_caster`` specialization @@ -126,10 +130,8 @@ The caster class defined above can be plugged into pybind11 in two ways: .. note:: ``type_caster` specializations may be full (as in this simple example) or partial. - .. warning:: - - When using this method, it is important to declare the specializations - consistently in all compilation units of a Python extension module. - Otherwise the One Definition Rule - (`ODR `_) - is violated, which can result in undefined behavior. +.. warning:: + With either alternative, for a given type ``T``, the ``pybind11_select_caster`` + declaration or ``type_caster`` specialization must be consistent across all compilation + units of a Python extension module, to satisfy the C++ One Definition Rule + (`ODR `_). From 8e8ac554e15e1c022d7dbef4bf70f72f60904c71 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 13:01:44 -0700 Subject: [PATCH 09/16] Use PYBIND11_MAYBE_UNUSED to get rid of the ugly dummy implementation. --- tests/test_make_caster_adl.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index f5c8a1ee58..eb4383bff6 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -25,16 +25,7 @@ struct unnamed_ns_type_mock {}; struct unnamed_ns_mock_caster { static int num() { return 303; } }; -unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); -#if !defined(_MSC_VER) -// Dummy implementation, purely to avoid compiler warnings (unused function). -// Easier than managing compiler-specific pragmas for warning suppression. -// MSVC happens to not generate any warnings. Leveraging that to prove that -// this test actually works without an implementation. -unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *) { - return unnamed_ns_mock_caster{}; -} -#endif +PYBIND11_MAYBE_UNUSED unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); } // namespace namespace mrc_ns { // minimal real caster @@ -84,9 +75,4 @@ TEST_SUBMODULE(make_caster_adl, m) { return obj; }); m.def("mrc_arg", [](mrc_ns::type_mrc const &obj) { return obj.value + 2000; }); - -#if !defined(_MSC_VER) - // Dummy call, purely to avoid compiler warnings (unused function). - (void) pybind11_select_caster(static_cast(nullptr)); -#endif } From 910a4aa74b911545e83dd728570a85cac6c2b688 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 13:47:29 -0700 Subject: [PATCH 10/16] Add -Wunused-function warning suppression for GCC. --- tests/test_make_caster_adl.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index eb4383bff6..2e0ea05548 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -25,7 +25,14 @@ struct unnamed_ns_type_mock {}; struct unnamed_ns_mock_caster { static int num() { return 303; } }; +#if !defined(PYBIND11_CPP17) && defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +#endif PYBIND11_MAYBE_UNUSED unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); +#if !defined(PYBIND11_CPP17) && defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif } // namespace namespace mrc_ns { // minimal real caster From 4fc1ddcfe3bed3c6588ebab565cdec0669fb03f1 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 15:49:45 -0700 Subject: [PATCH 11/16] Try simpler `#if` & permanent `ignored "-Wunused-function"` --- tests/test_make_caster_adl.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 2e0ea05548..00f364e008 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -25,14 +25,10 @@ struct unnamed_ns_type_mock {}; struct unnamed_ns_mock_caster { static int num() { return 303; } }; -#if !defined(PYBIND11_CPP17) && defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push +#if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic ignored "-Wunused-function" #endif PYBIND11_MAYBE_UNUSED unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); -#if !defined(PYBIND11_CPP17) && defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif } // namespace namespace mrc_ns { // minimal real caster From 974b02972032eceec5ee82e8389eacc468e0ccd8 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 16:32:51 -0700 Subject: [PATCH 12/16] Try `diagnostic pop` again. --- tests/test_make_caster_adl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 00f364e008..7b35bf6d56 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -25,10 +25,14 @@ struct unnamed_ns_type_mock {}; struct unnamed_ns_mock_caster { static int num() { return 303; } }; -#if defined(__GNUC__) && !defined(__clang__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" #endif PYBIND11_MAYBE_UNUSED unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *); +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# pragma GCC diagnostic pop +#endif } // namespace namespace mrc_ns { // minimal real caster From 9043841d837391a018424087eb3c56726241e1a0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 17:12:50 -0700 Subject: [PATCH 13/16] Add comment for pragma `#if`. --- tests/test_make_caster_adl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_make_caster_adl.cpp b/tests/test_make_caster_adl.cpp index 7b35bf6d56..52addcdf4f 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_make_caster_adl.cpp @@ -25,6 +25,8 @@ struct unnamed_ns_type_mock {}; struct unnamed_ns_mock_caster { static int num() { return 303; } }; +// Some GCCs may not need this, but there is very little to gain for making this condition more +// complex. #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" From 06571a4fa9e089b6e2c6913a1410e46b4c5e0459 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 6 May 2022 19:30:46 -0700 Subject: [PATCH 14/16] Add test_docs_advanced_cast_custom & update custom.rst code blocks. --- docs/advanced/cast/custom.rst | 73 +++++++++--------- tests/CMakeLists.txt | 1 + tests/test_docs_advanced_cast_custom.cpp | 98 ++++++++++++++++++++++++ tests/test_docs_advanced_cast_custom.py | 6 ++ 4 files changed, 142 insertions(+), 36 deletions(-) create mode 100644 tests/test_docs_advanced_cast_custom.cpp create mode 100644 tests/test_docs_advanced_cast_custom.py diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index d1bc902ead..1444e2adee 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -12,13 +12,21 @@ type that we want to be convertible to C++ from any Python type that provides an ``__int__`` method, and is converted to a Python ``int`` when returned from C++ to Python. +.. + PLEASE KEEP THE CODE BLOCKS IN SYNC WITH + tests/test_docs_advanced_cast_custom.cpp + Ideally, change the test, run pre-commit (incl. clang-format), + then copy the changed code back here. + .. code-block:: cpp namespace user_space { - struct inty { long long_value; }; + struct inty { + long long_value; + }; - void print(inty s) { std::cout << s.long_value << std::endl; } + std::string to_string(const inty &s) { return std::to_string(s.long_value); } inty return_42() { return inty{42}; } @@ -34,45 +42,35 @@ Starting with the example caster class: struct inty_type_caster { public: - /** - * This macro establishes the name 'inty' in - * function signatures and declares a local variable - * 'value' of type inty - */ - PYBIND11_TYPE_CASTER(inty, pybind11::const_name("inty")); - /** - * Conversion part 1 (Python->C++): convert a PyObject into a inty - * instance or return false upon failure. The second argument - * indicates whether implicit conversions should be applied. - */ + // This macro establishes the name 'inty' in function signatures and declares a local variable + // 'value' of type inty. + PYBIND11_TYPE_CASTER(inty, pybind11::detail::const_name("inty")); + + // Python -> C++: convert a PyObject into an inty instance or return false upon failure. The + // second argument indicates whether implicit conversions should be allowed. bool load(pybind11::handle src, bool) { - /* Extract PyObject from handle */ + // Extract PyObject from handle. PyObject *source = src.ptr(); - /* Try converting into a Python integer value */ + // Try converting into a Python integer value. PyObject *tmp = PyNumber_Long(source); if (!tmp) { return false; } - /* Now try to convert into a C++ int */ + // Now try to convert into a C++ int. value.long_value = PyLong_AsLong(tmp); Py_DECREF(tmp); - /* Ensure return code was OK (to avoid out-of-range errors etc) */ + // Ensure PyLong_AsLong succeeded (to catch out-of-range errors etc). if (PyErr_Occurred()) { PyErr_Clear(); return false; } return true; } - /** - * Conversion part 2 (C++ -> Python): convert an inty instance into - * a Python object. The second and third arguments are used to - * indicate the return value policy and parent object (for - * ``return_value_policy::reference_internal``) and are generally - * ignored by implicit casters. - */ - static pybind11::handle cast(inty src, - pybind11::return_value_policy /* policy */, - pybind11::handle /* parent */) { + // C++ -> Python: convert an inty instance into a Python object. The second and third arguments + // are used to indicate the return value policy and parent object (for + // return_value_policy::reference_internal) and are often ignored by custom casters. + static pybind11::handle + cast(inty src, pybind11::return_value_policy /* policy */, pybind11::handle /* parent */) { return PyLong_FromLong(src.long_value); } }; @@ -95,7 +93,7 @@ The caster class defined above can be plugged into pybind11 in two ways: namespace user_space { - inty_type_caster pybind11_select_caster(inty*); + inty_type_caster pybind11_select_caster(inty *); } // namespace user_space @@ -110,12 +108,12 @@ The caster class defined above can be plugged into pybind11 in two ways: .. code-block:: cpp - struct inty_type_caster; + struct inty_type_caster; - struct inty { - ... - friend inty_type_caster pybind11_select_caster(inty*); - }; + struct inty { + ... + friend inty_type_caster pybind11_select_caster(inty *); + }; * An older alternative is to specialize the ``pybind11::detail::type_caster`` template. Although the ``detail`` namespace is involved, adding a ``type_caster`` specialization @@ -123,9 +121,12 @@ The caster class defined above can be plugged into pybind11 in two ways: .. code-block:: cpp - namespace pybind11 { namespace detail { - template <> struct type_caster : user_space::inty_type_caster {}; - }} // namespace pybind11::detail + namespace pybind11 { + namespace detail { + template <> + struct type_caster : user_space::inty_type_caster {}; + } // namespace detail + } // namespace pybind11 .. note:: ``type_caster` specializations may be full (as in this simple example) or partial. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7275152640..ce22748abb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -128,6 +128,7 @@ set(PYBIND11_TEST_FILES test_custom_type_casters test_custom_type_setup test_docstring_options + test_docs_advanced_cast_custom test_eigen test_enum test_eval diff --git a/tests/test_docs_advanced_cast_custom.cpp b/tests/test_docs_advanced_cast_custom.cpp new file mode 100644 index 0000000000..58b7459042 --- /dev/null +++ b/tests/test_docs_advanced_cast_custom.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2022 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ######################################################################### +// PLEASE UPDATE docs/advanced/cast/custom.rst IF ANY CHANGES ARE MADE HERE. +// ######################################################################### + +#include "pybind11_tests.h" + +#include + +// 0: Use the older pybind11::detail::type_caster specialization mechanism. +// 1: Use pybind11_select_caster declaration in user_space. +// 2: Use pybind11_select_caster friend function declaration. +#ifndef PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE +# define PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE 1 +#endif + +namespace user_space { + +#if PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE == 2 +struct inty_type_caster; +#endif + +struct inty { + long long_value; +#if PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE == 2 + friend inty_type_caster pybind11_select_caster(inty *); +#endif +}; + +std::string to_string(const inty &s) { return std::to_string(s.long_value); } + +inty return_42() { return inty{42}; } + +} // namespace user_space + +namespace user_space { + +struct inty_type_caster { +public: + // This macro establishes the name 'inty' in function signatures and declares a local variable + // 'value' of type inty. + PYBIND11_TYPE_CASTER(inty, pybind11::detail::const_name("inty")); + + // Python -> C++: convert a PyObject into an inty instance or return false upon failure. The + // second argument indicates whether implicit conversions should be allowed. + bool load(pybind11::handle src, bool) { + // Extract PyObject from handle. + PyObject *source = src.ptr(); + // Try converting into a Python integer value. + PyObject *tmp = PyNumber_Long(source); + if (!tmp) { + return false; + } + // Now try to convert into a C++ int. + value.long_value = PyLong_AsLong(tmp); + Py_DECREF(tmp); + // Ensure PyLong_AsLong succeeded (to catch out-of-range errors etc). + if (PyErr_Occurred()) { + PyErr_Clear(); + return false; + } + return true; + } + // C++ -> Python: convert an inty instance into a Python object. The second and third arguments + // are used to indicate the return value policy and parent object (for + // return_value_policy::reference_internal) and are often ignored by custom casters. + static pybind11::handle + cast(inty src, pybind11::return_value_policy /* policy */, pybind11::handle /* parent */) { + return PyLong_FromLong(src.long_value); + } +}; + +} // namespace user_space + +#if PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE == 1 +namespace user_space { + +inty_type_caster pybind11_select_caster(inty *); + +} // namespace user_space +#endif + +#if PYBIND11_TEST_DOCS_ADVANCED_CAST_CUSTOM_ALTERNATIVE == 0 +namespace pybind11 { +namespace detail { +template <> +struct type_caster : user_space::inty_type_caster {}; +} // namespace detail +} // namespace pybind11 +#endif + +TEST_SUBMODULE(docs_advanced_cast_custom, m) { + m.def("to_string", user_space::to_string); + m.def("return_42", user_space::return_42); +} diff --git a/tests/test_docs_advanced_cast_custom.py b/tests/test_docs_advanced_cast_custom.py new file mode 100644 index 0000000000..5df41c07bf --- /dev/null +++ b/tests/test_docs_advanced_cast_custom.py @@ -0,0 +1,6 @@ +from pybind11_tests import docs_advanced_cast_custom as m + + +def test_all(): + assert m.to_string(135) == "135" + assert m.return_42() == 42 From 38fdea32b371176b75b2d347f50b6ec8834ff714 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 7 May 2022 02:09:50 -0700 Subject: [PATCH 15/16] Rename test_make_caster_adl to test_select_caster --- docs/advanced/cast/custom.rst | 2 ++ tests/CMakeLists.txt | 4 ++-- tests/test_docs_advanced_cast_custom.cpp | 1 + tests/{test_make_caster_adl.cpp => test_select_caster.cpp} | 6 ++++-- tests/{test_make_caster_adl.py => test_select_caster.py} | 4 ++-- ...t_make_caster_adl_alt.cpp => test_select_caster_alt.cpp} | 6 +++++- ...est_make_caster_adl_alt.py => test_select_caster_alt.py} | 2 +- 7 files changed, 17 insertions(+), 8 deletions(-) rename tests/{test_make_caster_adl.cpp => test_select_caster.cpp} (92%) rename tests/{test_make_caster_adl.py => test_select_caster.py} (81%) rename tests/{test_make_caster_adl_alt.cpp => test_select_caster_alt.cpp} (60%) rename tests/{test_make_caster_adl_alt.py => test_select_caster_alt.py} (54%) diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 1444e2adee..d1d852b8a3 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -34,6 +34,7 @@ C++ to Python. The necessary conversion routines are defined by a caster class, which is then "plugged into" pybind11 using one of two alternative mechanisms. + Starting with the example caster class: .. code-block:: cpp @@ -66,6 +67,7 @@ Starting with the example caster class: } return true; } + // C++ -> Python: convert an inty instance into a Python object. The second and third arguments // are used to indicate the return value policy and parent object (for // return_value_policy::reference_internal) and are often ignored by custom casters. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ce22748abb..dbfbefef6c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -138,8 +138,6 @@ set(PYBIND11_TEST_FILES test_iostream test_kwargs_and_defaults test_local_bindings - test_make_caster_adl - test_make_caster_adl_alt test_methods_and_attributes test_modules test_multiple_inheritance @@ -150,6 +148,8 @@ set(PYBIND11_TEST_FILES test_operator_overloading test_pickling test_pytypes + test_select_caster + test_select_caster_alt test_sequences_and_iterators test_smart_ptr test_stl diff --git a/tests/test_docs_advanced_cast_custom.cpp b/tests/test_docs_advanced_cast_custom.cpp index 58b7459042..e45b8b4e95 100644 --- a/tests/test_docs_advanced_cast_custom.cpp +++ b/tests/test_docs_advanced_cast_custom.cpp @@ -64,6 +64,7 @@ struct inty_type_caster { } return true; } + // C++ -> Python: convert an inty instance into a Python object. The second and third arguments // are used to indicate the return value policy and parent object (for // return_value_policy::reference_internal) and are often ignored by custom casters. diff --git a/tests/test_make_caster_adl.cpp b/tests/test_select_caster.cpp similarity index 92% rename from tests/test_make_caster_adl.cpp rename to tests/test_select_caster.cpp index 52addcdf4f..b5e55d46d6 100644 --- a/tests/test_make_caster_adl.cpp +++ b/tests/test_select_caster.cpp @@ -1,4 +1,6 @@ -// adl = Argument Dependent Lookup +// Copyright (c) 2022 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. #include "pybind11_tests.h" @@ -73,7 +75,7 @@ struct minimal_real_caster { } // namespace mrc_ns -TEST_SUBMODULE(make_caster_adl, m) { +TEST_SUBMODULE(select_caster, m) { m.def("have_a_ns_num", []() { return py::detail::make_caster::num(); }); m.def("global_ns_num", []() { return py::detail::make_caster::num(); }); m.def("unnamed_ns_num", []() { return py::detail::make_caster::num(); }); diff --git a/tests/test_make_caster_adl.py b/tests/test_select_caster.py similarity index 81% rename from tests/test_make_caster_adl.py rename to tests/test_select_caster.py index b53e2e884c..eb20c134a0 100644 --- a/tests/test_make_caster_adl.py +++ b/tests/test_select_caster.py @@ -1,7 +1,7 @@ import pytest -from pybind11_tests import make_caster_adl as m -from pybind11_tests import make_caster_adl_alt as m_alt +from pybind11_tests import select_caster as m +from pybind11_tests import select_caster_alt as m_alt def test_mock_casters(): diff --git a/tests/test_make_caster_adl_alt.cpp b/tests/test_select_caster_alt.cpp similarity index 60% rename from tests/test_make_caster_adl_alt.cpp rename to tests/test_select_caster_alt.cpp index b19851708a..423d81f580 100644 --- a/tests/test_make_caster_adl_alt.cpp +++ b/tests/test_select_caster_alt.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2022 The Pybind Development Team. +// All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + #include "pybind11_tests.h" namespace have_a_ns { @@ -10,6 +14,6 @@ struct type_mock { } // namespace have_a_ns -TEST_SUBMODULE(make_caster_adl_alt, m) { +TEST_SUBMODULE(select_caster_alt, m) { m.def("have_a_ns_num", []() { return py::detail::make_caster::num(); }); } diff --git a/tests/test_make_caster_adl_alt.py b/tests/test_select_caster_alt.py similarity index 54% rename from tests/test_make_caster_adl_alt.py rename to tests/test_select_caster_alt.py index 0170a4e108..42a625bf42 100644 --- a/tests/test_make_caster_adl_alt.py +++ b/tests/test_select_caster_alt.py @@ -1,4 +1,4 @@ -from pybind11_tests import make_caster_adl_alt as m +from pybind11_tests import select_caster_alt as m def test_mock_casters(): From a48f5cdecad3e3fc442a2884dbe76486769e06ec Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 13 May 2022 06:38:08 -0700 Subject: [PATCH 16/16] Removing test_select_caster_alt completely. For full background see: https://github.com/pybind/pybind11/pull/3931 TL;DR: The work under PR #3931 made it obvious that test_select_caster_alt was too simple for deriving meaningful conclusions. --- tests/CMakeLists.txt | 1 - tests/test_select_caster.py | 5 ----- tests/test_select_caster_alt.cpp | 19 ------------------- tests/test_select_caster_alt.py | 5 ----- 4 files changed, 30 deletions(-) delete mode 100644 tests/test_select_caster_alt.cpp delete mode 100644 tests/test_select_caster_alt.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dbfbefef6c..66ee50de68 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -149,7 +149,6 @@ set(PYBIND11_TEST_FILES test_pickling test_pytypes test_select_caster - test_select_caster_alt test_sequences_and_iterators test_smart_ptr test_stl diff --git a/tests/test_select_caster.py b/tests/test_select_caster.py index eb20c134a0..5d515d38ed 100644 --- a/tests/test_select_caster.py +++ b/tests/test_select_caster.py @@ -1,7 +1,6 @@ import pytest from pybind11_tests import select_caster as m -from pybind11_tests import select_caster_alt as m_alt def test_mock_casters(): @@ -10,10 +9,6 @@ def test_mock_casters(): assert m.unnamed_ns_num() == 303 -def test_mock_casters_alt(): - assert m_alt.have_a_ns_num() == 121 - - def test_minimal_real_caster(): assert m.mrc_return() == 1505 assert m.mrc_arg("ignored") == 2404 diff --git a/tests/test_select_caster_alt.cpp b/tests/test_select_caster_alt.cpp deleted file mode 100644 index 423d81f580..0000000000 --- a/tests/test_select_caster_alt.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2022 The Pybind Development Team. -// All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -#include "pybind11_tests.h" - -namespace have_a_ns { -struct mock_caster_alt { - static int num() { return 121; } -}; -struct type_mock { - friend mock_caster_alt pybind11_select_caster(type_mock *); -}; - -} // namespace have_a_ns - -TEST_SUBMODULE(select_caster_alt, m) { - m.def("have_a_ns_num", []() { return py::detail::make_caster::num(); }); -} diff --git a/tests/test_select_caster_alt.py b/tests/test_select_caster_alt.py deleted file mode 100644 index 42a625bf42..0000000000 --- a/tests/test_select_caster_alt.py +++ /dev/null @@ -1,5 +0,0 @@ -from pybind11_tests import select_caster_alt as m - - -def test_mock_casters(): - assert m.have_a_ns_num() == 121