mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-26 23:22:01 +00:00
6de6191a0c
* Try using `std::hash<std::type_index>`, `std::equal_to<std::type_index>` everywhere.
From PR #4316 we know that types in the unnamed namespace in different translation units do not compare equal, as desired.
But do types in named namespaces compare equal, as desired?
* Revert "Try using `std::hash<std::type_index>`, `std::equal_to<std::type_index>` everywhere."
This reverts commit a06949a926
.
* Use "our own name-based hash and equality functions" for `std::type_index` only under macOS, based on results shown under https://github.com/pybind/pybind11/pull/4316#issuecomment-1305097879
* Patch in PR #4313: Minimal reproducer for clash when binding types defined in the unnamed namespace.
* test_unnamed_namespace_b xfail for clang
* `PYBIND11_INTERNALS_VERSION 5`
* Add a note to docs/classes.rst
* For compatibility with Google-internal testing, test_unnamed_namespace_a & test_unnamed_namespace_b need to work when imported in any order.
* Trying "__GLIBCXX__ or Windows", based on observations from Google-internal testing.
* Try _LIBCPP_VERSION
* Account for libc++ behavior in tests and documentation.
* Adjust expectations for Windows Clang (and make code less redundant).
* Add WindowsClang to ci.yml
Added block transferred from PR #4321
* Add clang-latest to name that appears in the GitHub Actions web view.
* Tweak the note in classes.rst again.
* Add `pip install --upgrade pip`, Show env, cosmetic changes
Already tested under PR #4321
* Add macos_brew_install_llvm to ci.yml
Added block transferred from PR #4324
* `test_cross_module_exception_translator` xfail 'Homebrew Clang'
* Revert back to base version of .github/workflows/ci.yml (the ci.yml changes were merged under #4323 and #4326)
* Fixes for ruff
* Make updated condition in internals.h dependent on ABI version.
* Remove PYBIND11_TEST_OVERRIDE when testing with PYBIND11_INTERNALS_VERSION=10000000
* Selectively exercise cmake `-DPYBIND11_TEST_OVERRIDE`: ubuntu, macos, windows
Extra work added to quick jobs, based on timings below, to not increase the GHA start-to-last-job-finished time.
```
Duration
^ Number of pytest runs
^ ^ Job identifier
^ ^ ^
0:03:48.024227 1 1___3___Clang_3.6___C++11___x64.txt
0:03:58.992814 1 2___3___Clang_3.7___C++11___x64.txt
0:04:25.758942 1 1___3.7___Debian___x86____Install.txt
0:04:50.148276 1 4___3___Clang_7___C++11___x64.txt
0:04:55.784558 1 13___3___Clang_15___C++20___x64.txt
0:04:57.048754 1 6___3___Clang_dev___C++11___x64.txt
0:05:00.485181 1 7___3___Clang_5___C++14___x64.txt
0:05:03.744964 1 2___3___almalinux8___x64.txt
0:05:06.222752 1 5___3___Clang_9___C++11___x64.txt
0:05:11.767022 1 2___3___GCC_7___C++17__x64.txt
0:05:18.634930 1 2___3.11__deadsnakes____x64.txt
0:05:22.810995 1 1___3___GCC_7___C++11__x64.txt
0:05:25.275317 1 12___3___Clang_14___C++20___x64.txt
0:05:32.058174 1 5___3___GCC_10___C++17__x64.txt
0:05:39.381351 1 7___3___GCC_12___C++20__x64.txt
0:05:40.502252 1 8___3___Clang_10___C++17___x64.txt
0:05:59.344905 1 3___3___Clang_3.9___C++11___x64.txt
0:06:10.825147 1 6___3___GCC_11___C++20__x64.txt
0:06:20.655443 1 3___3___almalinux9___x64.txt
0:06:22.472061 1 3___3___GCC_8___C++14__x64.txt
0:06:42.647406 1 11___3___Clang_13___C++20___x64.txt
0:06:53.352720 1 1___3.10___CUDA_11.7___Ubuntu_22.04.txt
0:07:07.357801 1 2___3.7___MSVC_2019___x86_-DCMAKE_CXX_STANDARD=14.txt
0:07:09.057603 1 1___3___centos7___x64.txt
0:07:15.546282 1 1___3.8___MSVC_2019__Debug____x86_-DCMAKE_CXX_STANDARD=17.txt
0:07:22.566022 1 4___3___GCC_8___C++17__x64.txt
0:08:13.592674 1 2___3.9___MSVC_2019__Debug____x86_-DCMAKE_CXX_STANDARD=20.txt
0:08:16.422768 1 9___3___Clang_11___C++20___x64.txt
0:08:21.168457 1 3___3.8___MSVC_2019___x86_-DCMAKE_CXX_STANDARD=17.txt
0:08:27.129468 1 10___3___Clang_12___C++20___x64.txt
0:09:35.045470 1 1___3.10___windows-latest___clang-latest.txt
0:09:57.361843 1 1___3.9___MSVC_2022_C++20___x64.txt
0:10:35.187767 1 1___3.6___MSVC_2019___x86.txt
0:11:14.691200 4 2___3.9___ubuntu-20.04___x64.txt
0:11:37.701167 1 1_macos-latest___brew_install_llvm.txt
0:11:38.688299 4 4___3.11___ubuntu-20.04___x64.txt
0:11:52.720216 1 4___3.9___MSVC_2019___x86_-DCMAKE_CXX_STANDARD=20.txt
0:13:23.456591 4 6___pypy-3.8___ubuntu-20.04___x64_-DPYBIND11_FINDPYTHON=ON.txt
0:13:25.863592 2 1___3___ICC_latest___x64.txt
0:13:32.411758 3 9___3.9___windows-2022___x64.txt
0:13:45.473377 4 3___3.10___ubuntu-20.04___x64.txt
0:13:55.366447 4 5___pypy-3.7___ubuntu-20.04___x64.txt
0:13:57.969502 3 10___3.10___windows-2022___x64.txt
0:14:19.837475 3 11___3.11___windows-2022___x64.txt
0:14:33.316770 4 1___3.6___ubuntu-20.04___x64_-DPYBIND11_FINDPYTHON=ON_-DCMA.txt
0:15:34.449278 4 22___3.6___windows-2019___x64_-DPYBIND11_FINDPYTHON=ON.txt
0:16:25.189055 2 1___3.9-dbg__deadsnakes____Valgrind___x64.txt
0:17:20.956667 4 15___3.6___macos-latest___x64.txt
0:17:27.513891 4 23___3.9___windows-2019___x64.txt
0:17:58.783286 3 8___3.6___windows-2022___x64.txt
0:18:25.917828 4 7___pypy-3.9___ubuntu-20.04___x64.txt
0:19:17.399820 3 13___pypy-3.8___windows-2022___x64.txt
0:19:45.002122 3 12___pypy-3.7___windows-2022___x64.txt
0:20:03.201926 4 16___3.9___macos-latest___x64.txt
0:20:15.415178 4 17___3.10___macos-latest___x64.txt
0:20:20.263216 4 20___pypy-3.8___macos-latest___x64.txt
0:20:31.998226 3 1___3___windows-latest___mingw64.txt
0:20:40.812286 4 18___3.11___macos-latest___x64.txt
0:22:47.714749 4 19___pypy-3.7___macos-latest___x64.txt
0:23:04.435859 3 2___3___windows-latest___mingw32.txt
0:25:48.719597 3 14___pypy-3.9___windows-2022___x64.txt
0:26:01.211688 4 21___pypy-3.9___macos-latest___x64.txt
0:28:19.971015 1 1___3___CentOS7__PGI_22.9___x64.txt
```
* Update skipif for Python 3.12a7 (the WIP needs to be handled in a separate PR).
552 lines
16 KiB
ReStructuredText
552 lines
16 KiB
ReStructuredText
.. _classes:
|
|
|
|
Object-oriented code
|
|
####################
|
|
|
|
Creating bindings for a custom type
|
|
===================================
|
|
|
|
Let's now look at a more complex example where we'll create bindings for a
|
|
custom C++ data structure named ``Pet``. Its definition is given below:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct Pet {
|
|
Pet(const std::string &name) : name(name) { }
|
|
void setName(const std::string &name_) { name = name_; }
|
|
const std::string &getName() const { return name; }
|
|
|
|
std::string name;
|
|
};
|
|
|
|
The binding code for ``Pet`` looks as follows:
|
|
|
|
.. code-block:: cpp
|
|
|
|
#include <pybind11/pybind11.h>
|
|
|
|
namespace py = pybind11;
|
|
|
|
PYBIND11_MODULE(example, m) {
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &>())
|
|
.def("setName", &Pet::setName)
|
|
.def("getName", &Pet::getName);
|
|
}
|
|
|
|
:class:`class_` creates bindings for a C++ *class* or *struct*-style data
|
|
structure. :func:`init` is a convenience function that takes the types of a
|
|
constructor's parameters as template arguments and wraps the corresponding
|
|
constructor (see the :ref:`custom_constructors` section for details). An
|
|
interactive Python session demonstrating this example is shown below:
|
|
|
|
.. code-block:: pycon
|
|
|
|
% python
|
|
>>> import example
|
|
>>> p = example.Pet("Molly")
|
|
>>> print(p)
|
|
<example.Pet object at 0x10cd98060>
|
|
>>> p.getName()
|
|
'Molly'
|
|
>>> p.setName("Charly")
|
|
>>> p.getName()
|
|
'Charly'
|
|
|
|
.. seealso::
|
|
|
|
Static member functions can be bound in the same way using
|
|
:func:`class_::def_static`.
|
|
|
|
.. note::
|
|
|
|
Binding C++ types in unnamed namespaces (also known as anonymous namespaces)
|
|
works reliably on many platforms, but not all. The `XFAIL_CONDITION` in
|
|
tests/test_unnamed_namespace_a.py encodes the currently known conditions.
|
|
For background see `#4319 <https://github.com/pybind/pybind11/pull/4319>`_.
|
|
If portability is a concern, it is therefore not recommended to bind C++
|
|
types in unnamed namespaces. It will be safest to manually pick unique
|
|
namespace names.
|
|
|
|
Keyword and default arguments
|
|
=============================
|
|
It is possible to specify keyword and default arguments using the syntax
|
|
discussed in the previous chapter. Refer to the sections :ref:`keyword_args`
|
|
and :ref:`default_args` for details.
|
|
|
|
Binding lambda functions
|
|
========================
|
|
|
|
Note how ``print(p)`` produced a rather useless summary of our data structure in the example above:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> print(p)
|
|
<example.Pet object at 0x10cd98060>
|
|
|
|
To address this, we could bind a utility function that returns a human-readable
|
|
summary to the special method slot named ``__repr__``. Unfortunately, there is no
|
|
suitable functionality in the ``Pet`` data structure, and it would be nice if
|
|
we did not have to change it. This can easily be accomplished by binding a
|
|
Lambda function instead:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &>())
|
|
.def("setName", &Pet::setName)
|
|
.def("getName", &Pet::getName)
|
|
.def("__repr__",
|
|
[](const Pet &a) {
|
|
return "<example.Pet named '" + a.name + "'>";
|
|
}
|
|
);
|
|
|
|
Both stateless [#f1]_ and stateful lambda closures are supported by pybind11.
|
|
With the above change, the same Python code now produces the following output:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> print(p)
|
|
<example.Pet named 'Molly'>
|
|
|
|
.. [#f1] Stateless closures are those with an empty pair of brackets ``[]`` as the capture object.
|
|
|
|
.. _properties:
|
|
|
|
Instance and static fields
|
|
==========================
|
|
|
|
We can also directly expose the ``name`` field using the
|
|
:func:`class_::def_readwrite` method. A similar :func:`class_::def_readonly`
|
|
method also exists for ``const`` fields.
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &>())
|
|
.def_readwrite("name", &Pet::name)
|
|
// ... remainder ...
|
|
|
|
This makes it possible to write
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.Pet("Molly")
|
|
>>> p.name
|
|
'Molly'
|
|
>>> p.name = "Charly"
|
|
>>> p.name
|
|
'Charly'
|
|
|
|
Now suppose that ``Pet::name`` was a private internal variable
|
|
that can only be accessed via setters and getters.
|
|
|
|
.. code-block:: cpp
|
|
|
|
class Pet {
|
|
public:
|
|
Pet(const std::string &name) : name(name) { }
|
|
void setName(const std::string &name_) { name = name_; }
|
|
const std::string &getName() const { return name; }
|
|
private:
|
|
std::string name;
|
|
};
|
|
|
|
In this case, the method :func:`class_::def_property`
|
|
(:func:`class_::def_property_readonly` for read-only data) can be used to
|
|
provide a field-like interface within Python that will transparently call
|
|
the setter and getter functions:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &>())
|
|
.def_property("name", &Pet::getName, &Pet::setName)
|
|
// ... remainder ...
|
|
|
|
Write only properties can be defined by passing ``nullptr`` as the
|
|
input for the read function.
|
|
|
|
.. seealso::
|
|
|
|
Similar functions :func:`class_::def_readwrite_static`,
|
|
:func:`class_::def_readonly_static` :func:`class_::def_property_static`,
|
|
and :func:`class_::def_property_readonly_static` are provided for binding
|
|
static variables and properties. Please also see the section on
|
|
:ref:`static_properties` in the advanced part of the documentation.
|
|
|
|
Dynamic attributes
|
|
==================
|
|
|
|
Native Python classes can pick up new attributes dynamically:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> class Pet:
|
|
... name = "Molly"
|
|
...
|
|
>>> p = Pet()
|
|
>>> p.name = "Charly" # overwrite existing
|
|
>>> p.age = 2 # dynamically add a new attribute
|
|
|
|
By default, classes exported from C++ do not support this and the only writable
|
|
attributes are the ones explicitly defined using :func:`class_::def_readwrite`
|
|
or :func:`class_::def_property`.
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<>())
|
|
.def_readwrite("name", &Pet::name);
|
|
|
|
Trying to set any other attribute results in an error:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.Pet()
|
|
>>> p.name = "Charly" # OK, attribute defined in C++
|
|
>>> p.age = 2 # fail
|
|
AttributeError: 'Pet' object has no attribute 'age'
|
|
|
|
To enable dynamic attributes for C++ classes, the :class:`py::dynamic_attr` tag
|
|
must be added to the :class:`py::class_` constructor:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet", py::dynamic_attr())
|
|
.def(py::init<>())
|
|
.def_readwrite("name", &Pet::name);
|
|
|
|
Now everything works as expected:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.Pet()
|
|
>>> p.name = "Charly" # OK, overwrite value in C++
|
|
>>> p.age = 2 # OK, dynamically add a new attribute
|
|
>>> p.__dict__ # just like a native Python class
|
|
{'age': 2}
|
|
|
|
Note that there is a small runtime cost for a class with dynamic attributes.
|
|
Not only because of the addition of a ``__dict__``, but also because of more
|
|
expensive garbage collection tracking which must be activated to resolve
|
|
possible circular references. Native Python classes incur this same cost by
|
|
default, so this is not anything to worry about. By default, pybind11 classes
|
|
are more efficient than native Python classes. Enabling dynamic attributes
|
|
just brings them on par.
|
|
|
|
.. _inheritance:
|
|
|
|
Inheritance and automatic downcasting
|
|
=====================================
|
|
|
|
Suppose now that the example consists of two data structures with an
|
|
inheritance relationship:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct Pet {
|
|
Pet(const std::string &name) : name(name) { }
|
|
std::string name;
|
|
};
|
|
|
|
struct Dog : Pet {
|
|
Dog(const std::string &name) : Pet(name) { }
|
|
std::string bark() const { return "woof!"; }
|
|
};
|
|
|
|
There are two different ways of indicating a hierarchical relationship to
|
|
pybind11: the first specifies the C++ base class as an extra template
|
|
parameter of the :class:`class_`:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &>())
|
|
.def_readwrite("name", &Pet::name);
|
|
|
|
// Method 1: template parameter:
|
|
py::class_<Dog, Pet /* <- specify C++ parent type */>(m, "Dog")
|
|
.def(py::init<const std::string &>())
|
|
.def("bark", &Dog::bark);
|
|
|
|
Alternatively, we can also assign a name to the previously bound ``Pet``
|
|
:class:`class_` object and reference it when binding the ``Dog`` class:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet> pet(m, "Pet");
|
|
pet.def(py::init<const std::string &>())
|
|
.def_readwrite("name", &Pet::name);
|
|
|
|
// Method 2: pass parent class_ object:
|
|
py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
|
|
.def(py::init<const std::string &>())
|
|
.def("bark", &Dog::bark);
|
|
|
|
Functionality-wise, both approaches are equivalent. Afterwards, instances will
|
|
expose fields and methods of both types:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.Dog("Molly")
|
|
>>> p.name
|
|
'Molly'
|
|
>>> p.bark()
|
|
'woof!'
|
|
|
|
The C++ classes defined above are regular non-polymorphic types with an
|
|
inheritance relationship. This is reflected in Python:
|
|
|
|
.. code-block:: cpp
|
|
|
|
// Return a base pointer to a derived instance
|
|
m.def("pet_store", []() { return std::unique_ptr<Pet>(new Dog("Molly")); });
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.pet_store()
|
|
>>> type(p) # `Dog` instance behind `Pet` pointer
|
|
Pet # no pointer downcasting for regular non-polymorphic types
|
|
>>> p.bark()
|
|
AttributeError: 'Pet' object has no attribute 'bark'
|
|
|
|
The function returned a ``Dog`` instance, but because it's a non-polymorphic
|
|
type behind a base pointer, Python only sees a ``Pet``. In C++, a type is only
|
|
considered polymorphic if it has at least one virtual function and pybind11
|
|
will automatically recognize this:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct PolymorphicPet {
|
|
virtual ~PolymorphicPet() = default;
|
|
};
|
|
|
|
struct PolymorphicDog : PolymorphicPet {
|
|
std::string bark() const { return "woof!"; }
|
|
};
|
|
|
|
// Same binding code
|
|
py::class_<PolymorphicPet>(m, "PolymorphicPet");
|
|
py::class_<PolymorphicDog, PolymorphicPet>(m, "PolymorphicDog")
|
|
.def(py::init<>())
|
|
.def("bark", &PolymorphicDog::bark);
|
|
|
|
// Again, return a base pointer to a derived instance
|
|
m.def("pet_store2", []() { return std::unique_ptr<PolymorphicPet>(new PolymorphicDog); });
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = example.pet_store2()
|
|
>>> type(p)
|
|
PolymorphicDog # automatically downcast
|
|
>>> p.bark()
|
|
'woof!'
|
|
|
|
Given a pointer to a polymorphic base, pybind11 performs automatic downcasting
|
|
to the actual derived type. Note that this goes beyond the usual situation in
|
|
C++: we don't just get access to the virtual functions of the base, we get the
|
|
concrete derived type including functions and attributes that the base type may
|
|
not even be aware of.
|
|
|
|
.. seealso::
|
|
|
|
For more information about polymorphic behavior see :ref:`overriding_virtuals`.
|
|
|
|
|
|
Overloaded methods
|
|
==================
|
|
|
|
Sometimes there are several overloaded C++ methods with the same name taking
|
|
different kinds of input arguments:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct Pet {
|
|
Pet(const std::string &name, int age) : name(name), age(age) { }
|
|
|
|
void set(int age_) { age = age_; }
|
|
void set(const std::string &name_) { name = name_; }
|
|
|
|
std::string name;
|
|
int age;
|
|
};
|
|
|
|
Attempting to bind ``Pet::set`` will cause an error since the compiler does not
|
|
know which method the user intended to select. We can disambiguate by casting
|
|
them to function pointers. Binding multiple functions to the same Python name
|
|
automatically creates a chain of function overloads that will be tried in
|
|
sequence.
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def(py::init<const std::string &, int>())
|
|
.def("set", static_cast<void (Pet::*)(int)>(&Pet::set), "Set the pet's age")
|
|
.def("set", static_cast<void (Pet::*)(const std::string &)>(&Pet::set), "Set the pet's name");
|
|
|
|
The overload signatures are also visible in the method's docstring:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> help(example.Pet)
|
|
|
|
class Pet(__builtin__.object)
|
|
| Methods defined here:
|
|
|
|
|
| __init__(...)
|
|
| Signature : (Pet, str, int) -> NoneType
|
|
|
|
|
| set(...)
|
|
| 1. Signature : (Pet, int) -> NoneType
|
|
|
|
|
| Set the pet's age
|
|
|
|
|
| 2. Signature : (Pet, str) -> NoneType
|
|
|
|
|
| Set the pet's name
|
|
|
|
If you have a C++14 compatible compiler [#cpp14]_, you can use an alternative
|
|
syntax to cast the overloaded function:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def("set", py::overload_cast<int>(&Pet::set), "Set the pet's age")
|
|
.def("set", py::overload_cast<const std::string &>(&Pet::set), "Set the pet's name");
|
|
|
|
Here, ``py::overload_cast`` only requires the parameter types to be specified.
|
|
The return type and class are deduced. This avoids the additional noise of
|
|
``void (Pet::*)()`` as seen in the raw cast. If a function is overloaded based
|
|
on constness, the ``py::const_`` tag should be used:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct Widget {
|
|
int foo(int x, float y);
|
|
int foo(int x, float y) const;
|
|
};
|
|
|
|
py::class_<Widget>(m, "Widget")
|
|
.def("foo_mutable", py::overload_cast<int, float>(&Widget::foo))
|
|
.def("foo_const", py::overload_cast<int, float>(&Widget::foo, py::const_));
|
|
|
|
If you prefer the ``py::overload_cast`` syntax but have a C++11 compatible compiler only,
|
|
you can use ``py::detail::overload_cast_impl`` with an additional set of parentheses:
|
|
|
|
.. code-block:: cpp
|
|
|
|
template <typename... Args>
|
|
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
|
|
|
|
py::class_<Pet>(m, "Pet")
|
|
.def("set", overload_cast_<int>()(&Pet::set), "Set the pet's age")
|
|
.def("set", overload_cast_<const std::string &>()(&Pet::set), "Set the pet's name");
|
|
|
|
.. [#cpp14] A compiler which supports the ``-std=c++14`` flag.
|
|
|
|
.. note::
|
|
|
|
To define multiple overloaded constructors, simply declare one after the
|
|
other using the ``.def(py::init<...>())`` syntax. The existing machinery
|
|
for specifying keyword and default arguments also works.
|
|
|
|
Enumerations and internal types
|
|
===============================
|
|
|
|
Let's now suppose that the example class contains internal types like enumerations, e.g.:
|
|
|
|
.. code-block:: cpp
|
|
|
|
struct Pet {
|
|
enum Kind {
|
|
Dog = 0,
|
|
Cat
|
|
};
|
|
|
|
struct Attributes {
|
|
float age = 0;
|
|
};
|
|
|
|
Pet(const std::string &name, Kind type) : name(name), type(type) { }
|
|
|
|
std::string name;
|
|
Kind type;
|
|
Attributes attr;
|
|
};
|
|
|
|
The binding code for this example looks as follows:
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::class_<Pet> pet(m, "Pet");
|
|
|
|
pet.def(py::init<const std::string &, Pet::Kind>())
|
|
.def_readwrite("name", &Pet::name)
|
|
.def_readwrite("type", &Pet::type)
|
|
.def_readwrite("attr", &Pet::attr);
|
|
|
|
py::enum_<Pet::Kind>(pet, "Kind")
|
|
.value("Dog", Pet::Kind::Dog)
|
|
.value("Cat", Pet::Kind::Cat)
|
|
.export_values();
|
|
|
|
py::class_<Pet::Attributes>(pet, "Attributes")
|
|
.def(py::init<>())
|
|
.def_readwrite("age", &Pet::Attributes::age);
|
|
|
|
|
|
To ensure that the nested types ``Kind`` and ``Attributes`` are created within the scope of ``Pet``, the
|
|
``pet`` :class:`class_` instance must be supplied to the :class:`enum_` and :class:`class_`
|
|
constructor. The :func:`enum_::export_values` function exports the enum entries
|
|
into the parent scope, which should be skipped for newer C++11-style strongly
|
|
typed enums.
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = Pet("Lucy", Pet.Cat)
|
|
>>> p.type
|
|
Kind.Cat
|
|
>>> int(p.type)
|
|
1L
|
|
|
|
The entries defined by the enumeration type are exposed in the ``__members__`` property:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> Pet.Kind.__members__
|
|
{'Dog': Kind.Dog, 'Cat': Kind.Cat}
|
|
|
|
The ``name`` property returns the name of the enum value as a unicode string.
|
|
|
|
.. note::
|
|
|
|
It is also possible to use ``str(enum)``, however these accomplish different
|
|
goals. The following shows how these two approaches differ.
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> p = Pet("Lucy", Pet.Cat)
|
|
>>> pet_type = p.type
|
|
>>> pet_type
|
|
Pet.Cat
|
|
>>> str(pet_type)
|
|
'Pet.Cat'
|
|
>>> pet_type.name
|
|
'Cat'
|
|
|
|
.. note::
|
|
|
|
When the special tag ``py::arithmetic()`` is specified to the ``enum_``
|
|
constructor, pybind11 creates an enumeration that also supports rudimentary
|
|
arithmetic and bit-level operations like comparisons, and, or, xor, negation,
|
|
etc.
|
|
|
|
.. code-block:: cpp
|
|
|
|
py::enum_<Pet::Kind>(pet, "Kind", py::arithmetic())
|
|
...
|
|
|
|
By default, these are omitted to conserve space.
|