From f495dfc4339df1454620ac2e7c0f5561959e86af Mon Sep 17 00:00:00 2001 From: Eric Cousineau Date: Fri, 25 Feb 2022 16:25:23 -0500 Subject: [PATCH] cast: Qualify symbol usage in PYBIND11_TYPE_CASTER (#3758) * cast: Qualify symbol usage in PYBIND11_TYPE_CASTER Permits using macro outside of pybind11::detail * fixup! review --- include/pybind11/cast.h | 14 +++++++++----- tests/test_custom_type_casters.cpp | 31 ++++++++++++++++++++++++++++++ tests/test_custom_type_casters.py | 4 ++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index fad65f0fa..d45b49c52 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -85,11 +85,15 @@ protected: \ public: \ static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + template >::value, \ + int> = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ if (!src) \ - return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ auto h = cast(std::move(*src), policy, parent); \ delete src; \ return h; \ @@ -100,7 +104,7 @@ public: operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ template \ - using cast_op_type = pybind11::detail::movable_cast_op_type + using cast_op_type = ::pybind11::detail::movable_cast_op_type template using is_std_char_type = any_of, /* std::string */ diff --git a/tests/test_custom_type_casters.cpp b/tests/test_custom_type_casters.cpp index 1d6341582..25540e368 100644 --- a/tests/test_custom_type_casters.cpp +++ b/tests/test_custom_type_casters.cpp @@ -20,6 +20,7 @@ public: std::string arg = "(default arg inspector 2)"; }; class ArgAlwaysConverts {}; + namespace pybind11 { namespace detail { template <> @@ -105,6 +106,34 @@ struct type_caster { } // namespace detail } // namespace pybind11 +// Define type caster outside of `pybind11::detail` and then alias it. +namespace other_lib { +struct MyType {}; +// Corrupt `py` shorthand alias for surrounding context. +namespace py {} +// Corrupt unqualified relative `pybind11` namespace. +namespace pybind11 {} +// Correct alias. +namespace py_ = ::pybind11; +// Define caster. This is effectively no-op, we only ensure it compiles and we +// don't have any symbol collision when using macro mixin. +struct my_caster { + PYBIND11_TYPE_CASTER(MyType, py_::detail::const_name("MyType")); + bool load(py_::handle, bool) { return true; } + + static py_::handle cast(const MyType &, py_::return_value_policy, py_::handle) { + return py_::bool_(true).release(); + } +}; +} // namespace other_lib +// Effectively "alias" it into correct namespace (via inheritance). +namespace pybind11 { +namespace detail { +template <> +struct type_caster : public other_lib::my_caster {}; +} // namespace detail +} // namespace pybind11 + TEST_SUBMODULE(custom_type_casters, m) { // test_custom_type_casters @@ -175,4 +204,6 @@ TEST_SUBMODULE(custom_type_casters, m) { m.def("destruction_tester_cstats", &ConstructorStats::get, py::return_value_policy::reference); + + m.def("other_lib_type", [](other_lib::MyType x) { return x; }); } diff --git a/tests/test_custom_type_casters.py b/tests/test_custom_type_casters.py index bb47d01bd..adfa6cf86 100644 --- a/tests/test_custom_type_casters.py +++ b/tests/test_custom_type_casters.py @@ -114,3 +114,7 @@ def test_custom_caster_destruction(): # Make sure we still only have the original object (from ..._no_destroy()) alive: assert cstats.alive() == 1 + + +def test_custom_caster_other_lib(): + assert m.other_lib_type(True)