Skip to content

Commit fe02513

Browse files
committed
Reapplied changes from pybind#3674
1 parent 088ad4f commit fe02513

File tree

7 files changed

+239
-55
lines changed

7 files changed

+239
-55
lines changed

docs/advanced/cast/custom.rst

Lines changed: 68 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -31,63 +31,81 @@ The following Python snippet demonstrates the intended usage from the Python sid
3131
3232
print(A())
3333
34-
To register the necessary conversion routines, it is necessary to add an
35-
instantiation of the ``pybind11::detail::type_caster<T>`` template.
36-
Although this is an implementation detail, adding an instantiation of this
37-
type is explicitly allowed.
34+
To register the necessary conversion routines, it is necessary to define a
35+
caster class, and register it with the other pybind11 casters:
3836

3937
.. code-block:: cpp
4038
41-
namespace pybind11 { namespace detail {
42-
template <> struct type_caster<inty> {
43-
public:
44-
/**
45-
* This macro establishes the name 'inty' in
46-
* function signatures and declares a local variable
47-
* 'value' of type inty
48-
*/
49-
PYBIND11_TYPE_CASTER(inty, const_name("inty"));
50-
51-
/**
52-
* Conversion part 1 (Python->C++): convert a PyObject into a inty
53-
* instance or return false upon failure. The second argument
54-
* indicates whether implicit conversions should be applied.
55-
*/
56-
bool load(handle src, bool) {
57-
/* Extract PyObject from handle */
58-
PyObject *source = src.ptr();
59-
/* Try converting into a Python integer value */
60-
PyObject *tmp = PyNumber_Long(source);
61-
if (!tmp)
62-
return false;
63-
/* Now try to convert into a C++ int */
64-
value.long_value = PyLong_AsLong(tmp);
65-
Py_DECREF(tmp);
66-
/* Ensure return code was OK (to avoid out-of-range errors etc) */
67-
return !(value.long_value == -1 && !PyErr_Occurred());
68-
}
69-
70-
/**
71-
* Conversion part 2 (C++ -> Python): convert an inty instance into
72-
* a Python object. The second and third arguments are used to
73-
* indicate the return value policy and parent object (for
74-
* ``return_value_policy::reference_internal``) and are generally
75-
* ignored by implicit casters.
76-
*/
77-
static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) {
78-
return PyLong_FromLong(src.long_value);
79-
}
80-
};
81-
}} // namespace pybind11::detail
39+
struct inty_type_caster {
40+
public:
41+
/**
42+
* This macro establishes the name 'inty' in
43+
* function signatures and declares a local variable
44+
* 'value' of type inty
45+
*/
46+
PYBIND11_TYPE_CASTER(inty, const_name("inty"));
47+
/**
48+
* Conversion part 1 (Python->C++): convert a PyObject into a inty
49+
* instance or return false upon failure. The second argument
50+
* indicates whether implicit conversions should be applied.
51+
*/
52+
bool load(handle src, bool) {
53+
/* Extract PyObject from handle */
54+
PyObject *source = src.ptr();
55+
/* Try converting into a Python integer value */
56+
PyObject *tmp = PyNumber_Long(source);
57+
if (!tmp)
58+
return false;
59+
/* Now try to convert into a C++ int */
60+
value.long_value = PyLong_AsLong(tmp);
61+
Py_DECREF(tmp);
62+
/* Ensure return code was OK (to avoid out-of-range errors etc) */
63+
return !(value.long_value == -1 && !PyErr_Occurred());
64+
}
65+
/**
66+
* Conversion part 2 (C++ -> Python): convert an inty instance into
67+
* a Python object. The second and third arguments are used to
68+
* indicate the return value policy and parent object (for
69+
* ``return_value_policy::reference_internal``) and are generally
70+
* ignored by implicit casters.
71+
*/
72+
static handle cast(inty src, return_value_policy /* policy */, handle /* parent */) {
73+
return PyLong_FromLong(src.long_value);
74+
}
75+
};
8276
8377
.. note::
8478

85-
A ``type_caster<T>`` defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires
79+
A caster class defined with ``PYBIND11_TYPE_CASTER(T, ...)`` requires
8680
that ``T`` is default-constructible (``value`` is first default constructed
8781
and then ``load()`` assigns to it).
8882

89-
.. warning::
83+
The caster defined above must be registered with pybind11.
84+
There are two ways to do it:
9085

91-
When using custom type casters, it's important to declare them consistently
92-
in every compilation unit of the Python extension module. Otherwise,
93-
undefined behavior can ensue.
86+
* As an instantiation of the ``pybind11::detail::type_caster<T>`` template.
87+
Although this is an implementation detail, adding an instantiation of this
88+
type is explicitly allowed:
89+
90+
.. code-block:: cpp
91+
92+
namespace pybind11 { namespace detail {
93+
template <> struct type_caster<inty> : inty_type_caster {};
94+
}} // namespace pybind11::detail
95+
96+
.. warning::
97+
98+
When using this method, it's important to declare them consistently
99+
in every compilation unit of the Python extension module. Otherwise,
100+
undefined behavior can ensue.
101+
102+
* When you own the namespace where the type is defined, the preferred method
103+
is to *declare* a function named ``pybind11_select_caster``, its only purpose
104+
is to associate the C++ type with its caster class:
105+
106+
.. code-block:: cpp
107+
108+
inty_type_caster pybind11_select_caster(inty*);
109+
110+
The argument is a *pointer* to the C++ type, the return type is the
111+
caster type. This function has no implementation!

include/pybind11/cast.h

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,34 @@ PYBIND11_NAMESPACE_BEGIN(detail)
3333

3434
template <typename type, typename SFINAE = void>
3535
class type_caster : public type_caster_base<type> {};
36+
37+
template <typename IntrinsicType> type_caster<IntrinsicType> pybind11_select_caster(IntrinsicType *);
38+
39+
// MSVC 2015 generates an internal compiler error for the common code (in the #else branch below).
40+
// MSVC 2017 in C++14 mode produces incorrect code, leading to a tests/test_stl runtime failure.
41+
// Luckily, the workaround for MSVC 2015 also resolves the MSVC 2017 C++14 runtime failure.
42+
#if defined(_MSC_VER) && (_MSC_VER < 1910 || (_MSC_VER < 1920 && !defined(PYBIND11_CPP17)))
43+
44+
template <typename IntrinsicType, typename SFINAE = void>
45+
struct make_caster_impl;
46+
47+
template <typename IntrinsicType>
48+
struct make_caster_impl<IntrinsicType, enable_if_t< std::is_arithmetic<IntrinsicType>::value>>
49+
: type_caster<IntrinsicType> {};
50+
51+
template <typename IntrinsicType>
52+
struct make_caster_impl<IntrinsicType, enable_if_t<!std::is_arithmetic<IntrinsicType>::value>>
53+
: decltype(pybind11_select_caster(static_cast<IntrinsicType *>(nullptr))) {};
54+
3655
template <typename type>
37-
using make_caster = type_caster<intrinsic_t<type>>;
56+
using make_caster = make_caster_impl<intrinsic_t<type>>;
57+
58+
#else
59+
60+
template <typename type>
61+
using make_caster = decltype(pybind11_select_caster(static_cast<intrinsic_t<type> *>(nullptr)));
62+
63+
#endif
3864

3965
// Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T
4066
template <typename T>
@@ -998,8 +1024,8 @@ struct return_value_policy_override<
9981024
};
9991025

10001026
// Basic python -> C++ casting; throws if casting fails
1001-
template <typename T, typename SFINAE>
1002-
type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &handle) {
1027+
template <typename T, typename Caster>
1028+
Caster &load_type(Caster& conv, const handle& handle) {
10031029
if (!conv.load(handle, true)) {
10041030
#if defined(NDEBUG)
10051031
throw cast_error(
@@ -1012,11 +1038,17 @@ type_caster<T, SFINAE> &load_type(type_caster<T, SFINAE> &conv, const handle &ha
10121038
}
10131039
return conv;
10141040
}
1041+
1042+
template <typename T, typename SFINAE>
1043+
type_caster<T, SFINAE>& load_type(type_caster<T, SFINAE>& conv, const handle& handle) {
1044+
return load_type<T, type_caster<T, SFINAE>>(conv, handle);
1045+
}
1046+
10151047
// Wrapper around the above that also constructs and returns a type_caster
10161048
template <typename T>
10171049
make_caster<T> load_type(const handle &handle) {
10181050
make_caster<T> conv;
1019-
load_type(conv, handle);
1051+
load_type<T>(conv, handle);
10201052
return conv;
10211053
}
10221054

@@ -1143,7 +1175,7 @@ using override_caster_t = conditional_t<cast_is_temporary_value_reference<ret_ty
11431175
template <typename T>
11441176
enable_if_t<cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&o,
11451177
make_caster<T> &caster) {
1146-
return cast_op<T>(load_type(caster, o));
1178+
return cast_op<T>(load_type<T>(caster, o));
11471179
}
11481180
template <typename T>
11491181
enable_if_t<!cast_is_temporary_value_reference<T>::value, T> cast_ref(object &&,

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ set(PYBIND11_TEST_FILES
137137
test_iostream
138138
test_kwargs_and_defaults
139139
test_local_bindings
140+
test_make_caster_adl
141+
test_make_caster_adl_alt
140142
test_methods_and_attributes
141143
test_modules
142144
test_multiple_inheritance

tests/test_make_caster_adl.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// adl = Argument Dependent Lookup
2+
3+
#include "pybind11_tests.h"
4+
5+
namespace have_a_ns {
6+
struct type_mock {};
7+
struct mock_caster {
8+
static int num() { return 101; }
9+
};
10+
mock_caster pybind11_select_caster(type_mock *);
11+
} // namespace have_a_ns
12+
13+
// namespace global {
14+
struct global_ns_type_mock {};
15+
struct global_ns_mock_caster {
16+
static int num() { return 202; }
17+
};
18+
global_ns_mock_caster pybind11_select_caster(global_ns_type_mock *);
19+
// } // namespace global
20+
21+
namespace {
22+
struct unnamed_ns_type_mock {};
23+
struct unnamed_ns_mock_caster {
24+
static int num() { return 303; }
25+
};
26+
unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *);
27+
#if !defined(_MSC_VER)
28+
// Dummy implementation, purely to avoid compiler warnings (unused function).
29+
// Easier than managing compiler-specific pragmas for warning suppression.
30+
// MSVC happens to not generate any warnings. Leveraging that to prove that
31+
// this test actually works without an implementation.
32+
unnamed_ns_mock_caster pybind11_select_caster(unnamed_ns_type_mock *) {
33+
return unnamed_ns_mock_caster{};
34+
}
35+
#endif
36+
} // namespace
37+
38+
namespace mrc_ns { // minimal real caster
39+
40+
struct type_mrc {
41+
int value = -9999;
42+
};
43+
44+
struct minimal_real_caster {
45+
static constexpr auto name = py::detail::const_name<type_mrc>();
46+
47+
static py::handle
48+
cast(type_mrc const &src, py::return_value_policy /*policy*/, py::handle /*parent*/) {
49+
return py::int_(src.value + 1000).release();
50+
}
51+
52+
// Maximizing simplicity. This will go terribly wrong for other arg types.
53+
template <typename>
54+
using cast_op_type = const type_mrc &;
55+
56+
// NOLINTNEXTLINE(google-explicit-constructor)
57+
operator type_mrc const &() {
58+
static type_mrc obj;
59+
obj.value = 404;
60+
return obj;
61+
}
62+
63+
bool load(py::handle src, bool /*convert*/) {
64+
// Only accepts str, but the value is ignored.
65+
return py::isinstance<py::str>(src);
66+
}
67+
};
68+
69+
minimal_real_caster pybind11_select_caster(type_mrc *);
70+
71+
} // namespace mrc_ns
72+
73+
TEST_SUBMODULE(make_caster_adl, m) {
74+
m.def("have_a_ns_num", []() { return py::detail::make_caster<have_a_ns::type_mock>::num(); });
75+
m.def("global_ns_num", []() { return py::detail::make_caster<global_ns_type_mock>::num(); });
76+
m.def("unnamed_ns_num", []() { return py::detail::make_caster<unnamed_ns_type_mock>::num(); });
77+
78+
m.def("mrc_return", []() {
79+
mrc_ns::type_mrc obj;
80+
obj.value = 505;
81+
return obj;
82+
});
83+
m.def("mrc_arg", [](mrc_ns::type_mrc const &obj) { return obj.value + 2000; });
84+
85+
#if !defined(_MSC_VER)
86+
// Dummy call, purely to avoid compiler warnings (unused function).
87+
(void) pybind11_select_caster(static_cast<unnamed_ns_type_mock *>(nullptr));
88+
#endif
89+
}

tests/test_make_caster_adl.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from pybind11_tests import make_caster_adl as m
5+
from pybind11_tests import make_caster_adl_alt as m_alt
6+
7+
8+
def test_mock_casters():
9+
assert m.have_a_ns_num() == 101
10+
assert m.global_ns_num() == 202
11+
assert m.unnamed_ns_num() == 303
12+
13+
14+
def test_mock_casters_alt():
15+
assert m_alt.have_a_ns_num() == 121
16+
17+
18+
def test_minimal_real_caster():
19+
assert m.mrc_return() == 1505
20+
assert m.mrc_arg(u"ignored") == 2404
21+
with pytest.raises(TypeError) as excinfo:
22+
m.mrc_arg(None)
23+
assert "(arg0: mrc_ns::type_mrc) -> int" in str(excinfo.value)

tests/test_make_caster_adl_alt.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "pybind11_tests.h"
2+
3+
namespace have_a_ns {
4+
struct type_mock {};
5+
struct mock_caster_alt {
6+
static int num() { return 121; }
7+
};
8+
mock_caster_alt pybind11_select_caster(type_mock *);
9+
} // namespace have_a_ns
10+
11+
TEST_SUBMODULE(make_caster_adl_alt, m) {
12+
m.def("have_a_ns_num", []() { return py::detail::make_caster<have_a_ns::type_mock>::num(); });
13+
}

tests/test_make_caster_adl_alt.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from pybind11_tests import make_caster_adl_alt as m
4+
5+
6+
def test_mock_casters():
7+
assert m.have_a_ns_num() == 121

0 commit comments

Comments
 (0)