From 2b045757b5606eb55758cefb0453a7036f052866 Mon Sep 17 00:00:00 2001 From: Ivor Wanders Date: Mon, 10 Jun 2019 16:12:28 -0400 Subject: [PATCH] Improve documentation related to inheritance. (#1676) * Adds section to the reference. * Adds section to advanced classes page describing how to use `get_overload`. --- docs/advanced/classes.rst | 51 ++++++++++++++++++++++++++++++--- docs/faq.rst | 2 ++ docs/reference.rst | 15 ++++++++++ include/pybind11/pybind11.h | 57 +++++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 99a95c79e..d1f8849b4 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -80,10 +80,10 @@ helper class that is defined as follows: } }; -The macro :func:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual -functions, and :func:`PYBIND11_OVERLOAD` should be used for functions which have -a default implementation. There are also two alternate macros -:func:`PYBIND11_OVERLOAD_PURE_NAME` and :func:`PYBIND11_OVERLOAD_NAME` which +The macro :c:macro:`PYBIND11_OVERLOAD_PURE` should be used for pure virtual +functions, and :c:macro:`PYBIND11_OVERLOAD` should be used for functions which have +a default implementation. There are also two alternate macros +:c:macro:`PYBIND11_OVERLOAD_PURE_NAME` and :c:macro:`PYBIND11_OVERLOAD_NAME` which take a string-valued name argument between the *Parent class* and *Name of the function* slots, which defines the name of function in Python. This is required when the C++ and Python versions of the @@ -325,6 +325,10 @@ can now create a python class that inherits from ``Dog``: Extended trampoline class functionality ======================================= +.. _extended_class_functionality_forced_trampoline + +Forced trampoline class initialisation +-------------------------------------- The trampoline classes described in the previous sections are, by default, only initialized when needed. More specifically, they are initialized when a python class actually inherits from a registered type (instead of merely creating an @@ -352,6 +356,45 @@ ensuring member initialization and (eventual) destruction. See the file :file:`tests/test_virtual_functions.cpp` for complete examples showing both normal and forced trampoline instantiation. +Different method signatures +--------------------------- +The macro's introduced in :ref:`overriding_virtuals` cover most of the standard +use cases when exposing C++ classes to Python. Sometimes it is hard or unwieldy +to create a direct one-on-one mapping between the arguments and method return +type. + +An example would be when the C++ signature contains output arguments using +references (See also :ref:`faq_reference_arguments`). Another way of solving +this is to use the method body of the trampoline class to do conversions to the +input and return of the Python method. + +The main building block to do so is the :func:`get_overload`, this function +allows retrieving a method implemented in Python from within the trampoline's +methods. Consider for example a C++ method which has the signature +``bool myMethod(int32_t& value)``, where the return indicates whether +something should be done with the ``value``. This can be made convenient on the +Python side by allowing the Python function to return ``None`` or an ``int``: + +.. code-block:: cpp + + bool MyClass::myMethod(int32_t& value) + { + pybind11::gil_scoped_acquire gil; // Acquire the GIL while in this scope. + // Try to look up the overloaded method on the Python side. + pybind11::function overload = pybind11::get_overload(this, "myMethod"); + if (overload) { // method is found + auto obj = overload(value); // Call the Python function. + if (py::isinstance(obj)) { // check if it returned a Python integer type + value = obj.cast(); // Cast it and assign it to the value. + return true; // Return true; value should be used. + } else { + return false; // Python returned none, return false. + } + } + return false; // Alternatively return MyClass::myMethod(value); + } + + .. _custom_constructors: Custom constructors diff --git a/docs/faq.rst b/docs/faq.rst index 99a12cb31..93ccf10e5 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -39,6 +39,8 @@ multiple versions of Python and it finds the wrong one, delete cmake -DPYTHON_EXECUTABLE:FILEPATH= . +.. _faq_reference_arguments: + Limitations involving reference arguments ========================================= diff --git a/docs/reference.rst b/docs/reference.rst index 3f7484979..a9fbe6001 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -86,6 +86,21 @@ Python built-in functions .. doxygengroup:: python_builtins :members: +Inheritance +=========== + +See :doc:`/classes` and :doc:`/advanced/classes` for more detail. + +.. doxygendefine:: PYBIND11_OVERLOAD + +.. doxygendefine:: PYBIND11_OVERLOAD_PURE + +.. doxygendefine:: PYBIND11_OVERLOAD_NAME + +.. doxygendefine:: PYBIND11_OVERLOAD_PURE_NAME + +.. doxygenfunction:: get_overload + Exceptions ========== diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 3c83b5744..1b784673c 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2062,6 +2062,14 @@ inline function get_type_overload(const void *this_ptr, const detail::type_info return overload; } +/** \rst + Try to retrieve a python method by the provided name from the instance pointed to by the this_ptr. + + :this_ptr: The pointer to the object the overload should be retrieved for. This should be the first + non-trampoline class encountered in the inheritance chain. + :name: The name of the overloaded Python method to retrieve. + :return: The Python method by this name from the object or an empty function wrapper. + \endrst */ template function get_overload(const T *this_ptr, const char *name) { auto tinfo = detail::get_type_info(typeid(T)); return tinfo ? get_type_overload(this_ptr, tinfo, name) : function(); @@ -2080,17 +2088,66 @@ template function get_overload(const T *this_ptr, const char *name) { } \ } +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up a method named 'fn' + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. See :ref:`overriding_virtuals` for more information. This macro should be used when the method + name in C is not the same as the method name in Python. For example with `__str__`. + + .. code-block:: cpp + + std::string toString() override { + PYBIND11_OVERLOAD_NAME( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + toString, // Name of function in C++ (name) + "__str__", // Name of method in Python (fn) + ); + } +\endrst */ #define PYBIND11_OVERLOAD_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ return cname::fn(__VA_ARGS__) +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD_NAME`, except that it + throws if no overload can be found. +\endrst */ #define PYBIND11_OVERLOAD_PURE_NAME(ret_type, cname, name, fn, ...) \ PYBIND11_OVERLOAD_INT(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), name, __VA_ARGS__) \ pybind11::pybind11_fail("Tried to call pure virtual function \"" PYBIND11_STRINGIFY(cname) "::" name "\""); +/** \rst + Macro to populate the virtual method in the trampoline class. This macro tries to look up the method + from the Python side, deals with the :ref:`gil` and necessary argument conversions to call this method and return + the appropriate type. This macro should be used if the method name in C and in Python are identical. + See :ref:`overriding_virtuals` for more information. + + .. code-block:: cpp + + class PyAnimal : public Animal { + public: + // Inherit the constructors + using Animal::Animal; + + // Trampoline (need one for each virtual function) + std::string go(int n_times) override { + PYBIND11_OVERLOAD_PURE( + std::string, // Return type (ret_type) + Animal, // Parent class (cname) + go, // Name of function in C++ (must match Python name) (fn) + n_times // Argument(s) (...) + ); + } + }; +\endrst */ #define PYBIND11_OVERLOAD(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__) +/** \rst + Macro for pure virtual functions, this function is identical to :c:macro:`PYBIND11_OVERLOAD`, except that it throws + if no overload can be found. +\endrst */ #define PYBIND11_OVERLOAD_PURE(ret_type, cname, fn, ...) \ PYBIND11_OVERLOAD_PURE_NAME(PYBIND11_TYPE(ret_type), PYBIND11_TYPE(cname), #fn, fn, __VA_ARGS__)