diff --git a/docs/advanced/cast/custom.rst b/docs/advanced/cast/custom.rst index 1df4d3e14b..9dbd0f4482 100644 --- a/docs/advanced/cast/custom.rst +++ b/docs/advanced/cast/custom.rst @@ -31,63 +31,83 @@ 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. + +* 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 165102443c..89a6542923 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -31,7 +31,10 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) PYBIND11_NAMESPACE_BEGIN(detail) template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; + +template type_caster> pybind11_select_caster(type); + +template using make_caster = decltype(pybind11_select_caster(static_cast *>(nullptr))); // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T template typename make_caster::template cast_op_type cast_op(make_caster &caster) { @@ -443,21 +446,41 @@ template struct string_caster { bool load_bytes(enable_if_t::value, handle>) { return false; } }; +PYBIND11_NAMESPACE_END(detail) +PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) + +namespace std { + +// pybind11::detail::string_caster +// pybind11_select_caster(std::string*); template -struct type_caster, enable_if_t::value>> - : string_caster> {}; +pybind11::detail::enable_if_t< + pybind11::detail::is_std_char_type::value, + pybind11::detail::string_caster< + std::basic_string>> +pybind11_select_caster(std::basic_string*); #ifdef PYBIND11_HAS_STRING_VIEW +// pybind11::detail::string_caster +// pybind11_select_caster(std::string_view*); template -struct type_caster, enable_if_t::value>> - : string_caster, true> {}; +pybind11::detail::enable_if_t< + pybind11::detail::is_std_char_type::value, + pybind11::detail::string_caster< + std::basic_string_view, true>> +pybind11_select_caster(std::basic_string_view*); #endif +} // namespace std + +PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) +PYBIND11_NAMESPACE_BEGIN(detail) + // Type caster for C-style strings. We basically use a std::string type caster, but also add the // ability to use None as a nullptr char* (which the string caster doesn't allow). template struct type_caster::value>> { using StringType = std::basic_string; - using StringCaster = type_caster; + using StringCaster = make_caster; StringCaster str_caster; bool none = false; CharT one_char = 0; @@ -863,7 +886,7 @@ template struct return_value_policy_override 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(NDEBUG) throw cast_error("Unable to cast Python instance to C++ type (compile in debug mode for details)"); @@ -874,10 +897,13 @@ template type_caster &load_type(type_ca } 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; } @@ -964,7 +990,7 @@ template using override_caster_t = conditional_t< // Trampoline use: for reference/pointer types to value-converted values, we do a value cast, then // store the result in the given variable. For other types, this is a no-op. template 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 &&, override_unused &) { pybind11_fail("Internal error: cast_ref fallback invoked"); } @@ -972,11 +998,16 @@ template enable_if_t::value, // Trampoline use: Having a pybind11::cast with an invalid reference type is going to static_assert, even // though if it's in dead code, so we provide a "trampoline" to pybind11::cast that only does anything in // cases where pybind11::cast is valid. -template enable_if_t::value, T> cast_safe(object &&o) { - return pybind11::cast(std::move(o)); } -template enable_if_t::value, T> cast_safe(object &&) { - pybind11_fail("Internal error: cast_safe fallback invoked"); } -template <> inline void cast_safe(object &&) {} +template +enable_if_t>::value && + !cast_is_temporary_value_reference::value, T> +cast_safe(object &&o) { return pybind11::cast(std::move(o)); } +template +enable_if_t::value, T> +cast_safe(object &&) { pybind11_fail("Internal error: cast_safe fallback invoked"); } +template +enable_if_t>::value, void> +cast_safe(object &&) {} PYBIND11_NAMESPACE_END(detail)